{ "cells": [ { "cell_type": "markdown", "id": "9f97dd1e", "metadata": {}, "source": [ "# Libraries" ] }, { "cell_type": "code", "execution_count": 1, "id": "9e974df6", "metadata": {}, "outputs": [], "source": [ "from pathlib import Path\n", "from typing import TypedDict, List, Optional, Annotated, Literal\n", "from IPython.display import Image, display\n", "\n", "from langchain_core.documents import Document\n", "from langchain_core.messages import BaseMessage, SystemMessage, AIMessage, ToolMessage\n", "from langchain_core.tools import tool\n", "from langgraph.checkpoint.memory import InMemorySaver\n", "from langgraph.graph.message import add_messages\n", "from langchain_elasticsearch import ElasticsearchStore\n", "from langgraph.graph import StateGraph, END\n", "from langgraph.prebuilt import ToolNode, tools_condition\n", "from langfuse import Langfuse\n", "from langfuse.decorators import observe, langfuse_context\n", "\n", "from src.utils.llm_factory import create_chat_model\n", "from src.utils.emb_factory import create_embedding_model\n", "from src.config import (\n", " ELASTICSEARCH_LOCAL_URL,\n", " ELASTICSEARCH_INDEX,\n", " OLLAMA_MODEL_NAME,\n", " OLLAMA_EMB_MODEL_NAME\n", ")" ] }, { "cell_type": "code", "execution_count": 2, "id": "30edcecc", "metadata": {}, "outputs": [], "source": [ "langfuse = Langfuse()\n", "\n", "llm = create_chat_model(\n", " provider=\"ollama\",\n", " model=OLLAMA_MODEL_NAME,\n", " temperature=0.5,\n", " validate_model_on_init=True,\n", ")\n", "embeddings = create_embedding_model(\n", " provider=\"ollama\",\n", " model=OLLAMA_EMB_MODEL_NAME,\n", ")\n", "vector_store = ElasticsearchStore(\n", " es_url=ELASTICSEARCH_LOCAL_URL,\n", " index_name=ELASTICSEARCH_INDEX,\n", " embedding=embeddings,\n", " query_field=\"text\",\n", " vector_query_field=\"vector\",\n", " # strategy=ElasticsearchStore.ApproxRetrievalStrategy(\n", " # hybrid=True,\n", " # rrf={\"rank_constant\": 60, \"window_size\": 100}\n", " # )\n", ")" ] }, { "cell_type": "code", "execution_count": 3, "id": "ad98841b", "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Langfuse client is authenticated and ready!\n" ] } ], "source": [ "if langfuse.auth_check():\n", " print(\"Langfuse client is authenticated and ready!\")\n", "else:\n", " print(\"Authentication failed. Please check your credentials and host.\")" ] }, { "cell_type": "markdown", "id": "873ea2f6", "metadata": {}, "source": [ "### State" ] }, { "cell_type": "code", "execution_count": 4, "id": "5f8c88cf", "metadata": {}, "outputs": [], "source": [ "class AgentState(TypedDict):\n", " messages: Annotated[list, add_messages]\n", " reformulated_query: str\n", " context: str" ] }, { "cell_type": "code", "execution_count": 5, "id": "fd8ed542", "metadata": {}, "outputs": [], "source": [ "class AgenticAgentState(TypedDict):\n", " messages: Annotated[list, add_messages]" ] }, { "cell_type": "markdown", "id": "1d60c120", "metadata": {}, "source": [ "### Tools" ] }, { "cell_type": "code", "execution_count": 6, "id": "f0a21230", "metadata": {}, "outputs": [], "source": [ "retrieve_kwargs = {\"k\": 3}" ] }, { "cell_type": "code", "execution_count": 7, "id": "f9359747", "metadata": {}, "outputs": [], "source": [ "def format_context(docs: List[Document]) -> str:\n", " chunks: List[str] = []\n", " for i, doc in enumerate(docs, 1):\n", " source = (doc.metadata or {}).get(\"source\", \"Untitled\")\n", " source_id = (doc.metadata or {}).get(\"id\", f\"chunk-{i}\")\n", " text = doc.page_content or \"\"\n", " chunks.append(f\"[{i}] id={source_id} source={source}\\n{text}\")\n", " return \"\\n\\n\".join(chunks)\n", "\n", "\n", "@tool\n", "@observe(name=\"context_retrieve\")\n", "def context_retrieve(query: str) -> str:\n", " \"\"\"Consults vector store to respond AVAP related questions\n", " Args:\n", " query (str): The input query for which to retrieve relevant documents.\n", " \"\"\"\n", " retriever = vector_store.as_retriever(\n", " search_type=\"similarity\",\n", " search_kwargs=retrieve_kwargs,\n", " )\n", " docs = retriever.invoke(query)\n", " context = format_context(docs)\n", "\n", " langfuse_context.update_current_observation(\n", " input={\"query\": query, \"k\": retrieve_kwargs[\"k\"]},\n", " output={\n", " \"documents_count\": len(docs),\n", " \"sources\": [(doc.metadata or {}).get(\"source\", \"Untitled\") for doc in docs],\n", " \"document_ids\": [(doc.metadata or {}).get(\"id\", f\"chunk-{i+1}\") for i, doc in enumerate(docs)],\n", " \"context_preview\": context[:1000],\n", " },\n", " )\n", " return context" ] }, { "cell_type": "markdown", "id": "395966e2", "metadata": {}, "source": [ "### Agent" ] }, { "cell_type": "code", "execution_count": 8, "id": "66ae23f0", "metadata": {}, "outputs": [], "source": [ "REFORMULATE_PROMPT = SystemMessage(\n", " content=(\n", " \"You are a deterministic query rewriting function.\\n\"\n", " \"You convert natural language questions into keyword search queries.\\n\\n\"\n", " \"Strict constraints:\\n\"\n", " \"1. Keep function names and technical tokens unchanged.\\n\"\n", " \"2. Remove filler phrases.\\n\"\n", " \"3. Do not answer.\\n\"\n", " \"4. Do not explain.\\n\"\n", " \"5. Do not generate code.\\n\"\n", " \"6. Return a single-line query only.\\n\"\n", " \"7. If already optimal, return unchanged.\\n\"\n", " )\n", ")\n", "\n", "GENERATE_PROMPT = SystemMessage(\n", " content=\"\"\"You are an agent designed to assist users with AVAP (Advanced Virtual API Programming) language.\n", " It's a new language, so you should know nothing about it.\n", " Use ONLY the provided context to answer AVAP-related questions.\n", " If the context does not contain enough information, say so honestly.\n", " If the question is not related to AVAP, answer based on your general knowledge.\n", "\n", " Context:\n", " {context}\"\"\"\n", ")\n", "\n", "AGENTIC_PROMPT = SystemMessage(\n", " content=\"\"\"\n", " You are an assistant that helps users with AVAP (Advanced Virtual API Programming) language questions.\n", "\n", " AVAP is a completely new programming language and you have NO built-in knowledge about it.\n", "\n", " Rules:\n", "\n", " 1. If the user question is related to AVAP:\n", " - You must use the `context_retrieve` tool before answering.\n", " - The tool output is INTERNAL CONTEXT, not a user message.\n", " - Never thank the user for the retrieved context.\n", " - You must synthesize an answer to respond the user query based SOLELY on the retrieved context.\n", " \n", " 2. If the retrieved context is insufficient:\n", " - Call `context_retrieve` again with a better reformulated query.\n", "\n", " 3. If the question is not related to AVAP:\n", " - Answer normally using general knowledge.\n", "\n", " 4. If the user asks for code only:\n", " - Return only the code snippet.\n", " - Do not add explanation, headings, or markdown fences unless requested.\n", " \"\"\"\n", ")" ] }, { "cell_type": "code", "execution_count": 9, "id": "36d0f54e", "metadata": {}, "outputs": [], "source": [ "def reformulate(state: AgentState) -> AgentState:\n", " \"\"\"Use the LLM to rewrite the user query for better retrieval.\"\"\"\n", " user_msg = state[\"messages\"][-1]\n", " resp = llm.invoke([REFORMULATE_PROMPT, user_msg])\n", " reformulated = resp.content.strip()\n", " print(f\"[reformulate] '{user_msg.content}' → '{reformulated}'\")\n", " return {\"reformulated_query\": reformulated}\n", "\n", "\n", "def retrieve(state: AgentState) -> AgentState:\n", " \"\"\"Retrieve context using the reformulated query.\"\"\"\n", " query = state[\"reformulated_query\"]\n", " docs = vector_store.as_retriever(\n", " search_type=\"similarity\",\n", " search_kwargs=retrieve_kwargs,\n", " ).invoke(query)\n", " context = format_context(docs)\n", " print(f\"[retrieve] {len(docs)} docs fetched\")\n", " print(context)\n", " return {\"context\": context}\n", "\n", "\n", "def generate(state: AgentState) -> AgentState:\n", " \"\"\"Generate the final answer using retrieved context.\"\"\"\n", " prompt = SystemMessage(\n", " content=GENERATE_PROMPT.content.format(context=state[\"context\"])\n", " )\n", " resp = llm.invoke([prompt] + state[\"messages\"])\n", " return {\"messages\": [resp]}" ] }, { "cell_type": "code", "execution_count": 10, "id": "f073edc9", "metadata": {}, "outputs": [], "source": [ "tools = [context_retrieve]\n", "\n", "def agent(state: AgenticAgentState) -> AgenticAgentState:\n", " llm_with_tools = llm.bind_tools(tools)\n", " return {\"messages\": [llm_with_tools.invoke([SystemMessage(content=AGENTIC_PROMPT.content)] + state[\"messages\"])]}" ] }, { "cell_type": "markdown", "id": "ef55bca3", "metadata": {}, "source": [ "### Graph" ] }, { "cell_type": "code", "execution_count": 11, "id": "fae46a58", "metadata": {}, "outputs": [], "source": [ "memory = InMemorySaver()\n", "\n", "graph_builder = StateGraph(AgentState)\n", "\n", "graph_builder.add_node(\"reformulate\", reformulate)\n", "graph_builder.add_node(\"retrieve\", retrieve)\n", "graph_builder.add_node(\"generate\", generate)\n", "\n", "graph_builder.set_entry_point(\"reformulate\")\n", "graph_builder.add_edge(\"reformulate\", \"retrieve\")\n", "graph_builder.add_edge(\"retrieve\", \"generate\")\n", "graph_builder.add_edge(\"generate\", END)\n", "\n", "guided_graph = graph_builder.compile()" ] }, { "cell_type": "code", "execution_count": 12, "id": "7f57b543", "metadata": {}, "outputs": [ { "data": { "image/png": "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", "text/plain": [ "" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "try:\n", " display(Image(guided_graph.get_graph().draw_mermaid_png()))\n", "except Exception:\n", " pass" ] }, { "cell_type": "code", "execution_count": 13, "id": "f7a0993f", "metadata": {}, "outputs": [], "source": [ "tool_node = ToolNode(tools=tools)\n", "memory = InMemorySaver()\n", "\n", "graph_builder = StateGraph(AgenticAgentState)\n", "\n", "graph_builder.add_node(\"agent\", agent)\n", "graph_builder.add_node(\"tools\", tool_node)\n", "\n", "graph_builder.set_entry_point(\"agent\")\n", "graph_builder.add_conditional_edges(\n", " \"agent\",\n", " tools_condition,\n", ")\n", "graph_builder.add_edge(\"tools\", \"agent\")\n", "\n", "agentic_graph = graph_builder.compile()" ] }, { "cell_type": "code", "execution_count": 14, "id": "2fec3fdb", "metadata": {}, "outputs": [ { "data": { "image/png": "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", "text/plain": [ "" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "try:\n", " display(Image(agentic_graph.get_graph().draw_mermaid_png()))\n", "except Exception:\n", " pass" ] }, { "cell_type": "markdown", "id": "1e9aff05", "metadata": {}, "source": [ "### Test" ] }, { "cell_type": "code", "execution_count": 15, "id": "8569cf39", "metadata": {}, "outputs": [], "source": [ "@observe(name=\"graph_run\")\n", "def stream_graph_updates(user_input: str, graph: StateGraph):\n", " langfuse_context.update_current_trace(\n", " user_id=\"alberto\",\n", " tags=[\"avap\", \"rag\", \"langgraph\"],\n", " metadata={\"feature\": \"agentic-rag\"},\n", " )\n", "\n", " for event in graph.stream(\n", " {\"messages\": [{\"role\": \"user\", \"content\": user_input}]},\n", " stream_mode=\"values\",\n", " ):\n", " event[\"messages\"][-1].pretty_print()\n", "\n", " return event[\"messages\"][-1]" ] }, { "cell_type": "code", "execution_count": 16, "id": "a1a1f3cf", "metadata": {}, "outputs": [], "source": [ "user_input = \"\"\"What types of includes does AVAP have?\"\"\"" ] }, { "cell_type": "code", "execution_count": 18, "id": "53b89690", "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "================================\u001b[1m Human Message \u001b[0m=================================\n", "\n", "What types of includes does AVAP have?\n" ] }, { "ename": "ResponseError", "evalue": "failed to parse JSON: unexpected end of JSON input (status code: -1)", "output_type": "error", "traceback": [ "\u001b[31m---------------------------------------------------------------------------\u001b[39m", "\u001b[31mResponseError\u001b[39m Traceback (most recent call last)", "\u001b[36mCell\u001b[39m\u001b[36m \u001b[39m\u001b[32mIn[18]\u001b[39m\u001b[32m, line 1\u001b[39m\n\u001b[32m----> \u001b[39m\u001b[32m1\u001b[39m a = \u001b[43mstream_graph_updates\u001b[49m\u001b[43m(\u001b[49m\u001b[43muser_input\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43magentic_graph\u001b[49m\u001b[43m)\u001b[49m\n", "\u001b[36mFile \u001b[39m\u001b[32m~/PycharmProjects/assistance-engine/.venv/lib/python3.11/site-packages/langfuse/decorators/langfuse_decorator.py:256\u001b[39m, in \u001b[36mLangfuseDecorator._sync_observe..sync_wrapper\u001b[39m\u001b[34m(*args, **kwargs)\u001b[39m\n\u001b[32m 254\u001b[39m result = func(*args, **kwargs)\n\u001b[32m 255\u001b[39m \u001b[38;5;28;01mexcept\u001b[39;00m \u001b[38;5;167;01mException\u001b[39;00m \u001b[38;5;28;01mas\u001b[39;00m e:\n\u001b[32m--> \u001b[39m\u001b[32m256\u001b[39m \u001b[38;5;28;43mself\u001b[39;49m\u001b[43m.\u001b[49m\u001b[43m_handle_exception\u001b[49m\u001b[43m(\u001b[49m\u001b[43mobservation\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43me\u001b[49m\u001b[43m)\u001b[49m\n\u001b[32m 257\u001b[39m \u001b[38;5;28;01mfinally\u001b[39;00m:\n\u001b[32m 258\u001b[39m result = \u001b[38;5;28mself\u001b[39m._finalize_call(\n\u001b[32m 259\u001b[39m observation, result, capture_output, transform_to_string\n\u001b[32m 260\u001b[39m )\n", "\u001b[36mFile \u001b[39m\u001b[32m~/PycharmProjects/assistance-engine/.venv/lib/python3.11/site-packages/langfuse/decorators/langfuse_decorator.py:520\u001b[39m, in \u001b[36mLangfuseDecorator._handle_exception\u001b[39m\u001b[34m(self, observation, e)\u001b[39m\n\u001b[32m 516\u001b[39m \u001b[38;5;28;01mif\u001b[39;00m observation:\n\u001b[32m 517\u001b[39m _observation_params_context.get()[observation.id].update(\n\u001b[32m 518\u001b[39m level=\u001b[33m\"\u001b[39m\u001b[33mERROR\u001b[39m\u001b[33m\"\u001b[39m, status_message=\u001b[38;5;28mstr\u001b[39m(e)\n\u001b[32m 519\u001b[39m )\n\u001b[32m--> \u001b[39m\u001b[32m520\u001b[39m \u001b[38;5;28;01mraise\u001b[39;00m e\n", "\u001b[36mFile \u001b[39m\u001b[32m~/PycharmProjects/assistance-engine/.venv/lib/python3.11/site-packages/langfuse/decorators/langfuse_decorator.py:254\u001b[39m, in \u001b[36mLangfuseDecorator._sync_observe..sync_wrapper\u001b[39m\u001b[34m(*args, **kwargs)\u001b[39m\n\u001b[32m 251\u001b[39m result = \u001b[38;5;28;01mNone\u001b[39;00m\n\u001b[32m 253\u001b[39m \u001b[38;5;28;01mtry\u001b[39;00m:\n\u001b[32m--> \u001b[39m\u001b[32m254\u001b[39m result = \u001b[43mfunc\u001b[49m\u001b[43m(\u001b[49m\u001b[43m*\u001b[49m\u001b[43margs\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43m*\u001b[49m\u001b[43m*\u001b[49m\u001b[43mkwargs\u001b[49m\u001b[43m)\u001b[49m\n\u001b[32m 255\u001b[39m \u001b[38;5;28;01mexcept\u001b[39;00m \u001b[38;5;167;01mException\u001b[39;00m \u001b[38;5;28;01mas\u001b[39;00m e:\n\u001b[32m 256\u001b[39m \u001b[38;5;28mself\u001b[39m._handle_exception(observation, e)\n", "\u001b[36mCell\u001b[39m\u001b[36m \u001b[39m\u001b[32mIn[15]\u001b[39m\u001b[32m, line 9\u001b[39m, in \u001b[36mstream_graph_updates\u001b[39m\u001b[34m(user_input, graph)\u001b[39m\n\u001b[32m 1\u001b[39m \u001b[38;5;129m@observe\u001b[39m(name=\u001b[33m\"\u001b[39m\u001b[33mgraph_run\u001b[39m\u001b[33m\"\u001b[39m)\n\u001b[32m 2\u001b[39m \u001b[38;5;28;01mdef\u001b[39;00m\u001b[38;5;250m \u001b[39m\u001b[34mstream_graph_updates\u001b[39m(user_input: \u001b[38;5;28mstr\u001b[39m, graph: StateGraph):\n\u001b[32m 3\u001b[39m langfuse_context.update_current_trace(\n\u001b[32m 4\u001b[39m user_id=\u001b[33m\"\u001b[39m\u001b[33malberto\u001b[39m\u001b[33m\"\u001b[39m,\n\u001b[32m 5\u001b[39m tags=[\u001b[33m\"\u001b[39m\u001b[33mavap\u001b[39m\u001b[33m\"\u001b[39m, \u001b[33m\"\u001b[39m\u001b[33mrag\u001b[39m\u001b[33m\"\u001b[39m, \u001b[33m\"\u001b[39m\u001b[33mlanggraph\u001b[39m\u001b[33m\"\u001b[39m],\n\u001b[32m 6\u001b[39m metadata={\u001b[33m\"\u001b[39m\u001b[33mfeature\u001b[39m\u001b[33m\"\u001b[39m: \u001b[33m\"\u001b[39m\u001b[33magentic-rag\u001b[39m\u001b[33m\"\u001b[39m},\n\u001b[32m 7\u001b[39m )\n\u001b[32m----> \u001b[39m\u001b[32m9\u001b[39m \u001b[43m \u001b[49m\u001b[38;5;28;43;01mfor\u001b[39;49;00m\u001b[43m \u001b[49m\u001b[43mevent\u001b[49m\u001b[43m \u001b[49m\u001b[38;5;129;43;01min\u001b[39;49;00m\u001b[43m \u001b[49m\u001b[43mgraph\u001b[49m\u001b[43m.\u001b[49m\u001b[43mstream\u001b[49m\u001b[43m(\u001b[49m\n\u001b[32m 10\u001b[39m \u001b[43m \u001b[49m\u001b[43m{\u001b[49m\u001b[33;43m\"\u001b[39;49m\u001b[33;43mmessages\u001b[39;49m\u001b[33;43m\"\u001b[39;49m\u001b[43m:\u001b[49m\u001b[43m \u001b[49m\u001b[43m[\u001b[49m\u001b[43m{\u001b[49m\u001b[33;43m\"\u001b[39;49m\u001b[33;43mrole\u001b[39;49m\u001b[33;43m\"\u001b[39;49m\u001b[43m:\u001b[49m\u001b[43m \u001b[49m\u001b[33;43m\"\u001b[39;49m\u001b[33;43muser\u001b[39;49m\u001b[33;43m\"\u001b[39;49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[33;43m\"\u001b[39;49m\u001b[33;43mcontent\u001b[39;49m\u001b[33;43m\"\u001b[39;49m\u001b[43m:\u001b[49m\u001b[43m \u001b[49m\u001b[43muser_input\u001b[49m\u001b[43m}\u001b[49m\u001b[43m]\u001b[49m\u001b[43m}\u001b[49m\u001b[43m,\u001b[49m\n\u001b[32m 11\u001b[39m \u001b[43m \u001b[49m\u001b[43mstream_mode\u001b[49m\u001b[43m=\u001b[49m\u001b[33;43m\"\u001b[39;49m\u001b[33;43mvalues\u001b[39;49m\u001b[33;43m\"\u001b[39;49m\u001b[43m,\u001b[49m\n\u001b[32m 12\u001b[39m \u001b[43m \u001b[49m\u001b[43m)\u001b[49m\u001b[43m:\u001b[49m\n\u001b[32m 13\u001b[39m \u001b[43m \u001b[49m\u001b[43mevent\u001b[49m\u001b[43m[\u001b[49m\u001b[33;43m\"\u001b[39;49m\u001b[33;43mmessages\u001b[39;49m\u001b[33;43m\"\u001b[39;49m\u001b[43m]\u001b[49m\u001b[43m[\u001b[49m\u001b[43m-\u001b[49m\u001b[32;43m1\u001b[39;49m\u001b[43m]\u001b[49m\u001b[43m.\u001b[49m\u001b[43mpretty_print\u001b[49m\u001b[43m(\u001b[49m\u001b[43m)\u001b[49m\n\u001b[32m 15\u001b[39m \u001b[38;5;28;01mreturn\u001b[39;00m event[\u001b[33m\"\u001b[39m\u001b[33mmessages\u001b[39m\u001b[33m\"\u001b[39m][-\u001b[32m1\u001b[39m]\n", "\u001b[36mFile \u001b[39m\u001b[32m~/PycharmProjects/assistance-engine/.venv/lib/python3.11/site-packages/langgraph/pregel/main.py:2646\u001b[39m, in \u001b[36mPregel.stream\u001b[39m\u001b[34m(self, input, config, context, stream_mode, print_mode, output_keys, interrupt_before, interrupt_after, durability, subgraphs, debug, **kwargs)\u001b[39m\n\u001b[32m 2644\u001b[39m \u001b[38;5;28;01mfor\u001b[39;00m task \u001b[38;5;129;01min\u001b[39;00m loop.match_cached_writes():\n\u001b[32m 2645\u001b[39m loop.output_writes(task.id, task.writes, cached=\u001b[38;5;28;01mTrue\u001b[39;00m)\n\u001b[32m-> \u001b[39m\u001b[32m2646\u001b[39m \u001b[43m\u001b[49m\u001b[38;5;28;43;01mfor\u001b[39;49;00m\u001b[43m \u001b[49m\u001b[43m_\u001b[49m\u001b[43m \u001b[49m\u001b[38;5;129;43;01min\u001b[39;49;00m\u001b[43m \u001b[49m\u001b[43mrunner\u001b[49m\u001b[43m.\u001b[49m\u001b[43mtick\u001b[49m\u001b[43m(\u001b[49m\n\u001b[32m 2647\u001b[39m \u001b[43m \u001b[49m\u001b[43m[\u001b[49m\u001b[43mt\u001b[49m\u001b[43m \u001b[49m\u001b[38;5;28;43;01mfor\u001b[39;49;00m\u001b[43m \u001b[49m\u001b[43mt\u001b[49m\u001b[43m \u001b[49m\u001b[38;5;129;43;01min\u001b[39;49;00m\u001b[43m \u001b[49m\u001b[43mloop\u001b[49m\u001b[43m.\u001b[49m\u001b[43mtasks\u001b[49m\u001b[43m.\u001b[49m\u001b[43mvalues\u001b[49m\u001b[43m(\u001b[49m\u001b[43m)\u001b[49m\u001b[43m \u001b[49m\u001b[38;5;28;43;01mif\u001b[39;49;00m\u001b[43m \u001b[49m\u001b[38;5;129;43;01mnot\u001b[39;49;00m\u001b[43m \u001b[49m\u001b[43mt\u001b[49m\u001b[43m.\u001b[49m\u001b[43mwrites\u001b[49m\u001b[43m]\u001b[49m\u001b[43m,\u001b[49m\n\u001b[32m 2648\u001b[39m \u001b[43m \u001b[49m\u001b[43mtimeout\u001b[49m\u001b[43m=\u001b[49m\u001b[38;5;28;43mself\u001b[39;49m\u001b[43m.\u001b[49m\u001b[43mstep_timeout\u001b[49m\u001b[43m,\u001b[49m\n\u001b[32m 2649\u001b[39m \u001b[43m \u001b[49m\u001b[43mget_waiter\u001b[49m\u001b[43m=\u001b[49m\u001b[43mget_waiter\u001b[49m\u001b[43m,\u001b[49m\n\u001b[32m 2650\u001b[39m \u001b[43m \u001b[49m\u001b[43mschedule_task\u001b[49m\u001b[43m=\u001b[49m\u001b[43mloop\u001b[49m\u001b[43m.\u001b[49m\u001b[43maccept_push\u001b[49m\u001b[43m,\u001b[49m\n\u001b[32m 2651\u001b[39m \u001b[43m\u001b[49m\u001b[43m)\u001b[49m\u001b[43m:\u001b[49m\n\u001b[32m 2652\u001b[39m \u001b[43m \u001b[49m\u001b[38;5;66;43;03m# emit output\u001b[39;49;00m\n\u001b[32m 2653\u001b[39m \u001b[43m \u001b[49m\u001b[38;5;28;43;01myield from\u001b[39;49;00m\u001b[43m \u001b[49m\u001b[43m_output\u001b[49m\u001b[43m(\u001b[49m\n\u001b[32m 2654\u001b[39m \u001b[43m \u001b[49m\u001b[43mstream_mode\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mprint_mode\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43msubgraphs\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mstream\u001b[49m\u001b[43m.\u001b[49m\u001b[43mget\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mqueue\u001b[49m\u001b[43m.\u001b[49m\u001b[43mEmpty\u001b[49m\n\u001b[32m 2655\u001b[39m \u001b[43m \u001b[49m\u001b[43m)\u001b[49m\n\u001b[32m 2656\u001b[39m loop.after_tick()\n", "\u001b[36mFile \u001b[39m\u001b[32m~/PycharmProjects/assistance-engine/.venv/lib/python3.11/site-packages/langgraph/pregel/_runner.py:167\u001b[39m, in \u001b[36mPregelRunner.tick\u001b[39m\u001b[34m(self, tasks, reraise, timeout, retry_policy, get_waiter, schedule_task)\u001b[39m\n\u001b[32m 165\u001b[39m t = tasks[\u001b[32m0\u001b[39m]\n\u001b[32m 166\u001b[39m \u001b[38;5;28;01mtry\u001b[39;00m:\n\u001b[32m--> \u001b[39m\u001b[32m167\u001b[39m \u001b[43mrun_with_retry\u001b[49m\u001b[43m(\u001b[49m\n\u001b[32m 168\u001b[39m \u001b[43m \u001b[49m\u001b[43mt\u001b[49m\u001b[43m,\u001b[49m\n\u001b[32m 169\u001b[39m \u001b[43m \u001b[49m\u001b[43mretry_policy\u001b[49m\u001b[43m,\u001b[49m\n\u001b[32m 170\u001b[39m \u001b[43m \u001b[49m\u001b[43mconfigurable\u001b[49m\u001b[43m=\u001b[49m\u001b[43m{\u001b[49m\n\u001b[32m 171\u001b[39m \u001b[43m \u001b[49m\u001b[43mCONFIG_KEY_CALL\u001b[49m\u001b[43m:\u001b[49m\u001b[43m \u001b[49m\u001b[43mpartial\u001b[49m\u001b[43m(\u001b[49m\n\u001b[32m 172\u001b[39m \u001b[43m \u001b[49m\u001b[43m_call\u001b[49m\u001b[43m,\u001b[49m\n\u001b[32m 173\u001b[39m \u001b[43m \u001b[49m\u001b[43mweakref\u001b[49m\u001b[43m.\u001b[49m\u001b[43mref\u001b[49m\u001b[43m(\u001b[49m\u001b[43mt\u001b[49m\u001b[43m)\u001b[49m\u001b[43m,\u001b[49m\n\u001b[32m 174\u001b[39m \u001b[43m \u001b[49m\u001b[43mretry_policy\u001b[49m\u001b[43m=\u001b[49m\u001b[43mretry_policy\u001b[49m\u001b[43m,\u001b[49m\n\u001b[32m 175\u001b[39m \u001b[43m \u001b[49m\u001b[43mfutures\u001b[49m\u001b[43m=\u001b[49m\u001b[43mweakref\u001b[49m\u001b[43m.\u001b[49m\u001b[43mref\u001b[49m\u001b[43m(\u001b[49m\u001b[43mfutures\u001b[49m\u001b[43m)\u001b[49m\u001b[43m,\u001b[49m\n\u001b[32m 176\u001b[39m \u001b[43m \u001b[49m\u001b[43mschedule_task\u001b[49m\u001b[43m=\u001b[49m\u001b[43mschedule_task\u001b[49m\u001b[43m,\u001b[49m\n\u001b[32m 177\u001b[39m \u001b[43m \u001b[49m\u001b[43msubmit\u001b[49m\u001b[43m=\u001b[49m\u001b[38;5;28;43mself\u001b[39;49m\u001b[43m.\u001b[49m\u001b[43msubmit\u001b[49m\u001b[43m,\u001b[49m\n\u001b[32m 178\u001b[39m \u001b[43m \u001b[49m\u001b[43m)\u001b[49m\u001b[43m,\u001b[49m\n\u001b[32m 179\u001b[39m \u001b[43m \u001b[49m\u001b[43m}\u001b[49m\u001b[43m,\u001b[49m\n\u001b[32m 180\u001b[39m \u001b[43m \u001b[49m\u001b[43m)\u001b[49m\n\u001b[32m 181\u001b[39m \u001b[38;5;28mself\u001b[39m.commit(t, \u001b[38;5;28;01mNone\u001b[39;00m)\n\u001b[32m 182\u001b[39m \u001b[38;5;28;01mexcept\u001b[39;00m \u001b[38;5;167;01mException\u001b[39;00m \u001b[38;5;28;01mas\u001b[39;00m exc:\n", "\u001b[36mFile \u001b[39m\u001b[32m~/PycharmProjects/assistance-engine/.venv/lib/python3.11/site-packages/langgraph/pregel/_retry.py:42\u001b[39m, in \u001b[36mrun_with_retry\u001b[39m\u001b[34m(task, retry_policy, configurable)\u001b[39m\n\u001b[32m 40\u001b[39m task.writes.clear()\n\u001b[32m 41\u001b[39m \u001b[38;5;66;03m# run the task\u001b[39;00m\n\u001b[32m---> \u001b[39m\u001b[32m42\u001b[39m \u001b[38;5;28;01mreturn\u001b[39;00m \u001b[43mtask\u001b[49m\u001b[43m.\u001b[49m\u001b[43mproc\u001b[49m\u001b[43m.\u001b[49m\u001b[43minvoke\u001b[49m\u001b[43m(\u001b[49m\u001b[43mtask\u001b[49m\u001b[43m.\u001b[49m\u001b[43minput\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mconfig\u001b[49m\u001b[43m)\u001b[49m\n\u001b[32m 43\u001b[39m \u001b[38;5;28;01mexcept\u001b[39;00m ParentCommand \u001b[38;5;28;01mas\u001b[39;00m exc:\n\u001b[32m 44\u001b[39m ns: \u001b[38;5;28mstr\u001b[39m = config[CONF][CONFIG_KEY_CHECKPOINT_NS]\n", "\u001b[36mFile \u001b[39m\u001b[32m~/PycharmProjects/assistance-engine/.venv/lib/python3.11/site-packages/langgraph/_internal/_runnable.py:656\u001b[39m, in \u001b[36mRunnableSeq.invoke\u001b[39m\u001b[34m(self, input, config, **kwargs)\u001b[39m\n\u001b[32m 654\u001b[39m \u001b[38;5;66;03m# run in context\u001b[39;00m\n\u001b[32m 655\u001b[39m \u001b[38;5;28;01mwith\u001b[39;00m set_config_context(config, run) \u001b[38;5;28;01mas\u001b[39;00m context:\n\u001b[32m--> \u001b[39m\u001b[32m656\u001b[39m \u001b[38;5;28minput\u001b[39m = \u001b[43mcontext\u001b[49m\u001b[43m.\u001b[49m\u001b[43mrun\u001b[49m\u001b[43m(\u001b[49m\u001b[43mstep\u001b[49m\u001b[43m.\u001b[49m\u001b[43minvoke\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[38;5;28;43minput\u001b[39;49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mconfig\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43m*\u001b[49m\u001b[43m*\u001b[49m\u001b[43mkwargs\u001b[49m\u001b[43m)\u001b[49m\n\u001b[32m 657\u001b[39m \u001b[38;5;28;01melse\u001b[39;00m:\n\u001b[32m 658\u001b[39m \u001b[38;5;28minput\u001b[39m = step.invoke(\u001b[38;5;28minput\u001b[39m, config)\n", "\u001b[36mFile \u001b[39m\u001b[32m~/PycharmProjects/assistance-engine/.venv/lib/python3.11/site-packages/langgraph/_internal/_runnable.py:400\u001b[39m, in \u001b[36mRunnableCallable.invoke\u001b[39m\u001b[34m(self, input, config, **kwargs)\u001b[39m\n\u001b[32m 398\u001b[39m run_manager.on_chain_end(ret)\n\u001b[32m 399\u001b[39m \u001b[38;5;28;01melse\u001b[39;00m:\n\u001b[32m--> \u001b[39m\u001b[32m400\u001b[39m ret = \u001b[38;5;28;43mself\u001b[39;49m\u001b[43m.\u001b[49m\u001b[43mfunc\u001b[49m\u001b[43m(\u001b[49m\u001b[43m*\u001b[49m\u001b[43margs\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43m*\u001b[49m\u001b[43m*\u001b[49m\u001b[43mkwargs\u001b[49m\u001b[43m)\u001b[49m\n\u001b[32m 401\u001b[39m \u001b[38;5;28;01mif\u001b[39;00m \u001b[38;5;28mself\u001b[39m.recurse \u001b[38;5;129;01mand\u001b[39;00m \u001b[38;5;28misinstance\u001b[39m(ret, Runnable):\n\u001b[32m 402\u001b[39m \u001b[38;5;28;01mreturn\u001b[39;00m ret.invoke(\u001b[38;5;28minput\u001b[39m, config)\n", "\u001b[36mCell\u001b[39m\u001b[36m \u001b[39m\u001b[32mIn[10]\u001b[39m\u001b[32m, line 5\u001b[39m, in \u001b[36magent\u001b[39m\u001b[34m(state)\u001b[39m\n\u001b[32m 3\u001b[39m \u001b[38;5;28;01mdef\u001b[39;00m\u001b[38;5;250m \u001b[39m\u001b[34magent\u001b[39m(state: AgenticAgentState) -> AgenticAgentState:\n\u001b[32m 4\u001b[39m llm_with_tools = llm.bind_tools(tools)\n\u001b[32m----> \u001b[39m\u001b[32m5\u001b[39m \u001b[38;5;28;01mreturn\u001b[39;00m {\u001b[33m\"\u001b[39m\u001b[33mmessages\u001b[39m\u001b[33m\"\u001b[39m: [\u001b[43mllm_with_tools\u001b[49m\u001b[43m.\u001b[49m\u001b[43minvoke\u001b[49m\u001b[43m(\u001b[49m\u001b[43m[\u001b[49m\u001b[43mSystemMessage\u001b[49m\u001b[43m(\u001b[49m\u001b[43mcontent\u001b[49m\u001b[43m=\u001b[49m\u001b[43mAGENTIC_PROMPT\u001b[49m\u001b[43m.\u001b[49m\u001b[43mcontent\u001b[49m\u001b[43m)\u001b[49m\u001b[43m]\u001b[49m\u001b[43m \u001b[49m\u001b[43m+\u001b[49m\u001b[43m \u001b[49m\u001b[43mstate\u001b[49m\u001b[43m[\u001b[49m\u001b[33;43m\"\u001b[39;49m\u001b[33;43mmessages\u001b[39;49m\u001b[33;43m\"\u001b[39;49m\u001b[43m]\u001b[49m\u001b[43m)\u001b[49m]}\n", "\u001b[36mFile \u001b[39m\u001b[32m~/PycharmProjects/assistance-engine/.venv/lib/python3.11/site-packages/langchain_core/runnables/base.py:5695\u001b[39m, in \u001b[36mRunnableBindingBase.invoke\u001b[39m\u001b[34m(self, input, config, **kwargs)\u001b[39m\n\u001b[32m 5688\u001b[39m \u001b[38;5;129m@override\u001b[39m\n\u001b[32m 5689\u001b[39m \u001b[38;5;28;01mdef\u001b[39;00m\u001b[38;5;250m \u001b[39m\u001b[34minvoke\u001b[39m(\n\u001b[32m 5690\u001b[39m \u001b[38;5;28mself\u001b[39m,\n\u001b[32m (...)\u001b[39m\u001b[32m 5693\u001b[39m **kwargs: Any | \u001b[38;5;28;01mNone\u001b[39;00m,\n\u001b[32m 5694\u001b[39m ) -> Output:\n\u001b[32m-> \u001b[39m\u001b[32m5695\u001b[39m \u001b[38;5;28;01mreturn\u001b[39;00m \u001b[38;5;28;43mself\u001b[39;49m\u001b[43m.\u001b[49m\u001b[43mbound\u001b[49m\u001b[43m.\u001b[49m\u001b[43minvoke\u001b[49m\u001b[43m(\u001b[49m\n\u001b[32m 5696\u001b[39m \u001b[43m \u001b[49m\u001b[38;5;28;43minput\u001b[39;49m\u001b[43m,\u001b[49m\n\u001b[32m 5697\u001b[39m \u001b[43m \u001b[49m\u001b[38;5;28;43mself\u001b[39;49m\u001b[43m.\u001b[49m\u001b[43m_merge_configs\u001b[49m\u001b[43m(\u001b[49m\u001b[43mconfig\u001b[49m\u001b[43m)\u001b[49m\u001b[43m,\u001b[49m\n\u001b[32m 5698\u001b[39m \u001b[43m \u001b[49m\u001b[43m*\u001b[49m\u001b[43m*\u001b[49m\u001b[43m{\u001b[49m\u001b[43m*\u001b[49m\u001b[43m*\u001b[49m\u001b[38;5;28;43mself\u001b[39;49m\u001b[43m.\u001b[49m\u001b[43mkwargs\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43m*\u001b[49m\u001b[43m*\u001b[49m\u001b[43mkwargs\u001b[49m\u001b[43m}\u001b[49m\u001b[43m,\u001b[49m\n\u001b[32m 5699\u001b[39m \u001b[43m \u001b[49m\u001b[43m)\u001b[49m\n", "\u001b[36mFile \u001b[39m\u001b[32m~/PycharmProjects/assistance-engine/.venv/lib/python3.11/site-packages/langchain_core/language_models/chat_models.py:402\u001b[39m, in \u001b[36mBaseChatModel.invoke\u001b[39m\u001b[34m(self, input, config, stop, **kwargs)\u001b[39m\n\u001b[32m 388\u001b[39m \u001b[38;5;129m@override\u001b[39m\n\u001b[32m 389\u001b[39m \u001b[38;5;28;01mdef\u001b[39;00m\u001b[38;5;250m \u001b[39m\u001b[34minvoke\u001b[39m(\n\u001b[32m 390\u001b[39m \u001b[38;5;28mself\u001b[39m,\n\u001b[32m (...)\u001b[39m\u001b[32m 395\u001b[39m **kwargs: Any,\n\u001b[32m 396\u001b[39m ) -> AIMessage:\n\u001b[32m 397\u001b[39m config = ensure_config(config)\n\u001b[32m 398\u001b[39m \u001b[38;5;28;01mreturn\u001b[39;00m cast(\n\u001b[32m 399\u001b[39m \u001b[33m\"\u001b[39m\u001b[33mAIMessage\u001b[39m\u001b[33m\"\u001b[39m,\n\u001b[32m 400\u001b[39m cast(\n\u001b[32m 401\u001b[39m \u001b[33m\"\u001b[39m\u001b[33mChatGeneration\u001b[39m\u001b[33m\"\u001b[39m,\n\u001b[32m--> \u001b[39m\u001b[32m402\u001b[39m \u001b[38;5;28;43mself\u001b[39;49m\u001b[43m.\u001b[49m\u001b[43mgenerate_prompt\u001b[49m\u001b[43m(\u001b[49m\n\u001b[32m 403\u001b[39m \u001b[43m \u001b[49m\u001b[43m[\u001b[49m\u001b[38;5;28;43mself\u001b[39;49m\u001b[43m.\u001b[49m\u001b[43m_convert_input\u001b[49m\u001b[43m(\u001b[49m\u001b[38;5;28;43minput\u001b[39;49m\u001b[43m)\u001b[49m\u001b[43m]\u001b[49m\u001b[43m,\u001b[49m\n\u001b[32m 404\u001b[39m \u001b[43m \u001b[49m\u001b[43mstop\u001b[49m\u001b[43m=\u001b[49m\u001b[43mstop\u001b[49m\u001b[43m,\u001b[49m\n\u001b[32m 405\u001b[39m \u001b[43m \u001b[49m\u001b[43mcallbacks\u001b[49m\u001b[43m=\u001b[49m\u001b[43mconfig\u001b[49m\u001b[43m.\u001b[49m\u001b[43mget\u001b[49m\u001b[43m(\u001b[49m\u001b[33;43m\"\u001b[39;49m\u001b[33;43mcallbacks\u001b[39;49m\u001b[33;43m\"\u001b[39;49m\u001b[43m)\u001b[49m\u001b[43m,\u001b[49m\n\u001b[32m 406\u001b[39m \u001b[43m \u001b[49m\u001b[43mtags\u001b[49m\u001b[43m=\u001b[49m\u001b[43mconfig\u001b[49m\u001b[43m.\u001b[49m\u001b[43mget\u001b[49m\u001b[43m(\u001b[49m\u001b[33;43m\"\u001b[39;49m\u001b[33;43mtags\u001b[39;49m\u001b[33;43m\"\u001b[39;49m\u001b[43m)\u001b[49m\u001b[43m,\u001b[49m\n\u001b[32m 407\u001b[39m \u001b[43m \u001b[49m\u001b[43mmetadata\u001b[49m\u001b[43m=\u001b[49m\u001b[43mconfig\u001b[49m\u001b[43m.\u001b[49m\u001b[43mget\u001b[49m\u001b[43m(\u001b[49m\u001b[33;43m\"\u001b[39;49m\u001b[33;43mmetadata\u001b[39;49m\u001b[33;43m\"\u001b[39;49m\u001b[43m)\u001b[49m\u001b[43m,\u001b[49m\n\u001b[32m 408\u001b[39m \u001b[43m \u001b[49m\u001b[43mrun_name\u001b[49m\u001b[43m=\u001b[49m\u001b[43mconfig\u001b[49m\u001b[43m.\u001b[49m\u001b[43mget\u001b[49m\u001b[43m(\u001b[49m\u001b[33;43m\"\u001b[39;49m\u001b[33;43mrun_name\u001b[39;49m\u001b[33;43m\"\u001b[39;49m\u001b[43m)\u001b[49m\u001b[43m,\u001b[49m\n\u001b[32m 409\u001b[39m \u001b[43m \u001b[49m\u001b[43mrun_id\u001b[49m\u001b[43m=\u001b[49m\u001b[43mconfig\u001b[49m\u001b[43m.\u001b[49m\u001b[43mpop\u001b[49m\u001b[43m(\u001b[49m\u001b[33;43m\"\u001b[39;49m\u001b[33;43mrun_id\u001b[39;49m\u001b[33;43m\"\u001b[39;49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[38;5;28;43;01mNone\u001b[39;49;00m\u001b[43m)\u001b[49m\u001b[43m,\u001b[49m\n\u001b[32m 410\u001b[39m \u001b[43m \u001b[49m\u001b[43m*\u001b[49m\u001b[43m*\u001b[49m\u001b[43mkwargs\u001b[49m\u001b[43m,\u001b[49m\n\u001b[32m 411\u001b[39m \u001b[43m \u001b[49m\u001b[43m)\u001b[49m.generations[\u001b[32m0\u001b[39m][\u001b[32m0\u001b[39m],\n\u001b[32m 412\u001b[39m ).message,\n\u001b[32m 413\u001b[39m )\n", "\u001b[36mFile \u001b[39m\u001b[32m~/PycharmProjects/assistance-engine/.venv/lib/python3.11/site-packages/langchain_core/language_models/chat_models.py:1123\u001b[39m, in \u001b[36mBaseChatModel.generate_prompt\u001b[39m\u001b[34m(self, prompts, stop, callbacks, **kwargs)\u001b[39m\n\u001b[32m 1114\u001b[39m \u001b[38;5;129m@override\u001b[39m\n\u001b[32m 1115\u001b[39m \u001b[38;5;28;01mdef\u001b[39;00m\u001b[38;5;250m \u001b[39m\u001b[34mgenerate_prompt\u001b[39m(\n\u001b[32m 1116\u001b[39m \u001b[38;5;28mself\u001b[39m,\n\u001b[32m (...)\u001b[39m\u001b[32m 1120\u001b[39m **kwargs: Any,\n\u001b[32m 1121\u001b[39m ) -> LLMResult:\n\u001b[32m 1122\u001b[39m prompt_messages = [p.to_messages() \u001b[38;5;28;01mfor\u001b[39;00m p \u001b[38;5;129;01min\u001b[39;00m prompts]\n\u001b[32m-> \u001b[39m\u001b[32m1123\u001b[39m \u001b[38;5;28;01mreturn\u001b[39;00m \u001b[38;5;28;43mself\u001b[39;49m\u001b[43m.\u001b[49m\u001b[43mgenerate\u001b[49m\u001b[43m(\u001b[49m\u001b[43mprompt_messages\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mstop\u001b[49m\u001b[43m=\u001b[49m\u001b[43mstop\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mcallbacks\u001b[49m\u001b[43m=\u001b[49m\u001b[43mcallbacks\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43m*\u001b[49m\u001b[43m*\u001b[49m\u001b[43mkwargs\u001b[49m\u001b[43m)\u001b[49m\n", "\u001b[36mFile \u001b[39m\u001b[32m~/PycharmProjects/assistance-engine/.venv/lib/python3.11/site-packages/langchain_core/language_models/chat_models.py:933\u001b[39m, in \u001b[36mBaseChatModel.generate\u001b[39m\u001b[34m(self, messages, stop, callbacks, tags, metadata, run_name, run_id, **kwargs)\u001b[39m\n\u001b[32m 930\u001b[39m \u001b[38;5;28;01mfor\u001b[39;00m i, m \u001b[38;5;129;01min\u001b[39;00m \u001b[38;5;28menumerate\u001b[39m(input_messages):\n\u001b[32m 931\u001b[39m \u001b[38;5;28;01mtry\u001b[39;00m:\n\u001b[32m 932\u001b[39m results.append(\n\u001b[32m--> \u001b[39m\u001b[32m933\u001b[39m \u001b[38;5;28;43mself\u001b[39;49m\u001b[43m.\u001b[49m\u001b[43m_generate_with_cache\u001b[49m\u001b[43m(\u001b[49m\n\u001b[32m 934\u001b[39m \u001b[43m \u001b[49m\u001b[43mm\u001b[49m\u001b[43m,\u001b[49m\n\u001b[32m 935\u001b[39m \u001b[43m \u001b[49m\u001b[43mstop\u001b[49m\u001b[43m=\u001b[49m\u001b[43mstop\u001b[49m\u001b[43m,\u001b[49m\n\u001b[32m 936\u001b[39m \u001b[43m \u001b[49m\u001b[43mrun_manager\u001b[49m\u001b[43m=\u001b[49m\u001b[43mrun_managers\u001b[49m\u001b[43m[\u001b[49m\u001b[43mi\u001b[49m\u001b[43m]\u001b[49m\u001b[43m \u001b[49m\u001b[38;5;28;43;01mif\u001b[39;49;00m\u001b[43m \u001b[49m\u001b[43mrun_managers\u001b[49m\u001b[43m \u001b[49m\u001b[38;5;28;43;01melse\u001b[39;49;00m\u001b[43m \u001b[49m\u001b[38;5;28;43;01mNone\u001b[39;49;00m\u001b[43m,\u001b[49m\n\u001b[32m 937\u001b[39m \u001b[43m \u001b[49m\u001b[43m*\u001b[49m\u001b[43m*\u001b[49m\u001b[43mkwargs\u001b[49m\u001b[43m,\u001b[49m\n\u001b[32m 938\u001b[39m \u001b[43m \u001b[49m\u001b[43m)\u001b[49m\n\u001b[32m 939\u001b[39m )\n\u001b[32m 940\u001b[39m \u001b[38;5;28;01mexcept\u001b[39;00m \u001b[38;5;167;01mBaseException\u001b[39;00m \u001b[38;5;28;01mas\u001b[39;00m e:\n\u001b[32m 941\u001b[39m \u001b[38;5;28;01mif\u001b[39;00m run_managers:\n", "\u001b[36mFile \u001b[39m\u001b[32m~/PycharmProjects/assistance-engine/.venv/lib/python3.11/site-packages/langchain_core/language_models/chat_models.py:1235\u001b[39m, in \u001b[36mBaseChatModel._generate_with_cache\u001b[39m\u001b[34m(self, messages, stop, run_manager, **kwargs)\u001b[39m\n\u001b[32m 1233\u001b[39m result = generate_from_stream(\u001b[38;5;28miter\u001b[39m(chunks))\n\u001b[32m 1234\u001b[39m \u001b[38;5;28;01melif\u001b[39;00m inspect.signature(\u001b[38;5;28mself\u001b[39m._generate).parameters.get(\u001b[33m\"\u001b[39m\u001b[33mrun_manager\u001b[39m\u001b[33m\"\u001b[39m):\n\u001b[32m-> \u001b[39m\u001b[32m1235\u001b[39m result = \u001b[38;5;28;43mself\u001b[39;49m\u001b[43m.\u001b[49m\u001b[43m_generate\u001b[49m\u001b[43m(\u001b[49m\n\u001b[32m 1236\u001b[39m \u001b[43m \u001b[49m\u001b[43mmessages\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mstop\u001b[49m\u001b[43m=\u001b[49m\u001b[43mstop\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mrun_manager\u001b[49m\u001b[43m=\u001b[49m\u001b[43mrun_manager\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43m*\u001b[49m\u001b[43m*\u001b[49m\u001b[43mkwargs\u001b[49m\n\u001b[32m 1237\u001b[39m \u001b[43m \u001b[49m\u001b[43m)\u001b[49m\n\u001b[32m 1238\u001b[39m \u001b[38;5;28;01melse\u001b[39;00m:\n\u001b[32m 1239\u001b[39m result = \u001b[38;5;28mself\u001b[39m._generate(messages, stop=stop, **kwargs)\n", "\u001b[36mFile \u001b[39m\u001b[32m~/PycharmProjects/assistance-engine/.venv/lib/python3.11/site-packages/langchain_ollama/chat_models.py:1030\u001b[39m, in \u001b[36mChatOllama._generate\u001b[39m\u001b[34m(self, messages, stop, run_manager, **kwargs)\u001b[39m\n\u001b[32m 1023\u001b[39m \u001b[38;5;28;01mdef\u001b[39;00m\u001b[38;5;250m \u001b[39m\u001b[34m_generate\u001b[39m(\n\u001b[32m 1024\u001b[39m \u001b[38;5;28mself\u001b[39m,\n\u001b[32m 1025\u001b[39m messages: \u001b[38;5;28mlist\u001b[39m[BaseMessage],\n\u001b[32m (...)\u001b[39m\u001b[32m 1028\u001b[39m **kwargs: Any,\n\u001b[32m 1029\u001b[39m ) -> ChatResult:\n\u001b[32m-> \u001b[39m\u001b[32m1030\u001b[39m final_chunk = \u001b[38;5;28;43mself\u001b[39;49m\u001b[43m.\u001b[49m\u001b[43m_chat_stream_with_aggregation\u001b[49m\u001b[43m(\u001b[49m\n\u001b[32m 1031\u001b[39m \u001b[43m \u001b[49m\u001b[43mmessages\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mstop\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mrun_manager\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mverbose\u001b[49m\u001b[43m=\u001b[49m\u001b[38;5;28;43mself\u001b[39;49m\u001b[43m.\u001b[49m\u001b[43mverbose\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43m*\u001b[49m\u001b[43m*\u001b[49m\u001b[43mkwargs\u001b[49m\n\u001b[32m 1032\u001b[39m \u001b[43m \u001b[49m\u001b[43m)\u001b[49m\n\u001b[32m 1033\u001b[39m generation_info = final_chunk.generation_info\n\u001b[32m 1034\u001b[39m chat_generation = ChatGeneration(\n\u001b[32m 1035\u001b[39m message=AIMessage(\n\u001b[32m 1036\u001b[39m content=final_chunk.text,\n\u001b[32m (...)\u001b[39m\u001b[32m 1043\u001b[39m generation_info=generation_info,\n\u001b[32m 1044\u001b[39m )\n", "\u001b[36mFile \u001b[39m\u001b[32m~/PycharmProjects/assistance-engine/.venv/lib/python3.11/site-packages/langchain_ollama/chat_models.py:965\u001b[39m, in \u001b[36mChatOllama._chat_stream_with_aggregation\u001b[39m\u001b[34m(self, messages, stop, run_manager, verbose, **kwargs)\u001b[39m\n\u001b[32m 956\u001b[39m \u001b[38;5;28;01mdef\u001b[39;00m\u001b[38;5;250m \u001b[39m\u001b[34m_chat_stream_with_aggregation\u001b[39m(\n\u001b[32m 957\u001b[39m \u001b[38;5;28mself\u001b[39m,\n\u001b[32m 958\u001b[39m messages: \u001b[38;5;28mlist\u001b[39m[BaseMessage],\n\u001b[32m (...)\u001b[39m\u001b[32m 962\u001b[39m **kwargs: Any,\n\u001b[32m 963\u001b[39m ) -> ChatGenerationChunk:\n\u001b[32m 964\u001b[39m final_chunk = \u001b[38;5;28;01mNone\u001b[39;00m\n\u001b[32m--> \u001b[39m\u001b[32m965\u001b[39m \u001b[43m \u001b[49m\u001b[38;5;28;43;01mfor\u001b[39;49;00m\u001b[43m \u001b[49m\u001b[43mchunk\u001b[49m\u001b[43m \u001b[49m\u001b[38;5;129;43;01min\u001b[39;49;00m\u001b[43m \u001b[49m\u001b[38;5;28;43mself\u001b[39;49m\u001b[43m.\u001b[49m\u001b[43m_iterate_over_stream\u001b[49m\u001b[43m(\u001b[49m\u001b[43mmessages\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mstop\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43m*\u001b[49m\u001b[43m*\u001b[49m\u001b[43mkwargs\u001b[49m\u001b[43m)\u001b[49m\u001b[43m:\u001b[49m\n\u001b[32m 966\u001b[39m \u001b[43m \u001b[49m\u001b[38;5;28;43;01mif\u001b[39;49;00m\u001b[43m \u001b[49m\u001b[43mfinal_chunk\u001b[49m\u001b[43m \u001b[49m\u001b[38;5;129;43;01mis\u001b[39;49;00m\u001b[43m \u001b[49m\u001b[38;5;28;43;01mNone\u001b[39;49;00m\u001b[43m:\u001b[49m\n\u001b[32m 967\u001b[39m \u001b[43m \u001b[49m\u001b[43mfinal_chunk\u001b[49m\u001b[43m \u001b[49m\u001b[43m=\u001b[49m\u001b[43m \u001b[49m\u001b[43mchunk\u001b[49m\n", "\u001b[36mFile \u001b[39m\u001b[32m~/PycharmProjects/assistance-engine/.venv/lib/python3.11/site-packages/langchain_ollama/chat_models.py:1054\u001b[39m, in \u001b[36mChatOllama._iterate_over_stream\u001b[39m\u001b[34m(self, messages, stop, **kwargs)\u001b[39m\n\u001b[32m 1047\u001b[39m \u001b[38;5;28;01mdef\u001b[39;00m\u001b[38;5;250m \u001b[39m\u001b[34m_iterate_over_stream\u001b[39m(\n\u001b[32m 1048\u001b[39m \u001b[38;5;28mself\u001b[39m,\n\u001b[32m 1049\u001b[39m messages: \u001b[38;5;28mlist\u001b[39m[BaseMessage],\n\u001b[32m 1050\u001b[39m stop: \u001b[38;5;28mlist\u001b[39m[\u001b[38;5;28mstr\u001b[39m] | \u001b[38;5;28;01mNone\u001b[39;00m = \u001b[38;5;28;01mNone\u001b[39;00m,\n\u001b[32m 1051\u001b[39m **kwargs: Any,\n\u001b[32m 1052\u001b[39m ) -> Iterator[ChatGenerationChunk]:\n\u001b[32m 1053\u001b[39m reasoning = kwargs.get(\u001b[33m\"\u001b[39m\u001b[33mreasoning\u001b[39m\u001b[33m\"\u001b[39m, \u001b[38;5;28mself\u001b[39m.reasoning)\n\u001b[32m-> \u001b[39m\u001b[32m1054\u001b[39m \u001b[43m \u001b[49m\u001b[38;5;28;43;01mfor\u001b[39;49;00m\u001b[43m \u001b[49m\u001b[43mstream_resp\u001b[49m\u001b[43m \u001b[49m\u001b[38;5;129;43;01min\u001b[39;49;00m\u001b[43m \u001b[49m\u001b[38;5;28;43mself\u001b[39;49m\u001b[43m.\u001b[49m\u001b[43m_create_chat_stream\u001b[49m\u001b[43m(\u001b[49m\u001b[43mmessages\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mstop\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43m*\u001b[49m\u001b[43m*\u001b[49m\u001b[43mkwargs\u001b[49m\u001b[43m)\u001b[49m\u001b[43m:\u001b[49m\n\u001b[32m 1055\u001b[39m \u001b[43m \u001b[49m\u001b[38;5;28;43;01mif\u001b[39;49;00m\u001b[43m \u001b[49m\u001b[38;5;129;43;01mnot\u001b[39;49;00m\u001b[43m \u001b[49m\u001b[38;5;28;43misinstance\u001b[39;49m\u001b[43m(\u001b[49m\u001b[43mstream_resp\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[38;5;28;43mstr\u001b[39;49m\u001b[43m)\u001b[49m\u001b[43m:\u001b[49m\n\u001b[32m 1056\u001b[39m \u001b[43m \u001b[49m\u001b[43mcontent\u001b[49m\u001b[43m \u001b[49m\u001b[43m=\u001b[49m\u001b[43m \u001b[49m\u001b[43m(\u001b[49m\n\u001b[32m 1057\u001b[39m \u001b[43m \u001b[49m\u001b[43mstream_resp\u001b[49m\u001b[43m[\u001b[49m\u001b[33;43m\"\u001b[39;49m\u001b[33;43mmessage\u001b[39;49m\u001b[33;43m\"\u001b[39;49m\u001b[43m]\u001b[49m\u001b[43m[\u001b[49m\u001b[33;43m\"\u001b[39;49m\u001b[33;43mcontent\u001b[39;49m\u001b[33;43m\"\u001b[39;49m\u001b[43m]\u001b[49m\n\u001b[32m 1058\u001b[39m \u001b[43m \u001b[49m\u001b[38;5;28;43;01mif\u001b[39;49;00m\u001b[43m \u001b[49m\u001b[33;43m\"\u001b[39;49m\u001b[33;43mmessage\u001b[39;49m\u001b[33;43m\"\u001b[39;49m\u001b[43m \u001b[49m\u001b[38;5;129;43;01min\u001b[39;49;00m\u001b[43m \u001b[49m\u001b[43mstream_resp\u001b[49m\u001b[43m \u001b[49m\u001b[38;5;129;43;01mand\u001b[39;49;00m\u001b[43m \u001b[49m\u001b[33;43m\"\u001b[39;49m\u001b[33;43mcontent\u001b[39;49m\u001b[33;43m\"\u001b[39;49m\u001b[43m \u001b[49m\u001b[38;5;129;43;01min\u001b[39;49;00m\u001b[43m \u001b[49m\u001b[43mstream_resp\u001b[49m\u001b[43m[\u001b[49m\u001b[33;43m\"\u001b[39;49m\u001b[33;43mmessage\u001b[39;49m\u001b[33;43m\"\u001b[39;49m\u001b[43m]\u001b[49m\n\u001b[32m 1059\u001b[39m \u001b[43m \u001b[49m\u001b[38;5;28;43;01melse\u001b[39;49;00m\u001b[43m \u001b[49m\u001b[33;43m\"\u001b[39;49m\u001b[33;43m\"\u001b[39;49m\n\u001b[32m 1060\u001b[39m \u001b[43m \u001b[49m\u001b[43m)\u001b[49m\n", "\u001b[36mFile \u001b[39m\u001b[32m~/PycharmProjects/assistance-engine/.venv/lib/python3.11/site-packages/langchain_ollama/chat_models.py:952\u001b[39m, in \u001b[36mChatOllama._create_chat_stream\u001b[39m\u001b[34m(self, messages, stop, **kwargs)\u001b[39m\n\u001b[32m 950\u001b[39m \u001b[38;5;28;01mif\u001b[39;00m chat_params[\u001b[33m\"\u001b[39m\u001b[33mstream\u001b[39m\u001b[33m\"\u001b[39m]:\n\u001b[32m 951\u001b[39m \u001b[38;5;28;01mif\u001b[39;00m \u001b[38;5;28mself\u001b[39m._client:\n\u001b[32m--> \u001b[39m\u001b[32m952\u001b[39m \u001b[38;5;28;01myield from\u001b[39;00m \u001b[38;5;28mself\u001b[39m._client.chat(**chat_params)\n\u001b[32m 953\u001b[39m \u001b[38;5;28;01melif\u001b[39;00m \u001b[38;5;28mself\u001b[39m._client:\n\u001b[32m 954\u001b[39m \u001b[38;5;28;01myield\u001b[39;00m \u001b[38;5;28mself\u001b[39m._client.chat(**chat_params)\n", "\u001b[36mFile \u001b[39m\u001b[32m~/PycharmProjects/assistance-engine/.venv/lib/python3.11/site-packages/ollama/_client.py:184\u001b[39m, in \u001b[36mClient._request..inner\u001b[39m\u001b[34m()\u001b[39m\n\u001b[32m 182\u001b[39m part = json.loads(line)\n\u001b[32m 183\u001b[39m \u001b[38;5;28;01mif\u001b[39;00m err := part.get(\u001b[33m'\u001b[39m\u001b[33merror\u001b[39m\u001b[33m'\u001b[39m):\n\u001b[32m--> \u001b[39m\u001b[32m184\u001b[39m \u001b[38;5;28;01mraise\u001b[39;00m ResponseError(err)\n\u001b[32m 185\u001b[39m \u001b[38;5;28;01myield\u001b[39;00m \u001b[38;5;28mcls\u001b[39m(**part)\n", "\u001b[31mResponseError\u001b[39m: failed to parse JSON: unexpected end of JSON input (status code: -1)", "During task with name 'agent' and id '9110cf29-5205-b67b-0456-234df433158a'" ] } ], "source": [ "a = stream_graph_updates(user_input, agentic_graph)" ] }, { "cell_type": "code", "execution_count": null, "id": "d6b4da6a", "metadata": {}, "outputs": [], "source": [ "result = agentic_graph.invoke({\"messages\": [{\"role\": \"user\", \"content\": user_input}]})\n", "print(\"Final result:\")\n", "result[\"messages\"][-1].pretty_print()" ] }, { "cell_type": "code", "execution_count": null, "id": "2342b1f1", "metadata": {}, "outputs": [], "source": [] }, { "cell_type": "markdown", "id": "3707574b", "metadata": {}, "source": [ "### MTEB" ] }, { "cell_type": "code", "execution_count": 18, "id": "d9657ec4", "metadata": {}, "outputs": [ { "ename": "SyntaxError", "evalue": "invalid syntax (2793878467.py, line 12)", "output_type": "error", "traceback": [ " \u001b[36mCell\u001b[39m\u001b[36m \u001b[39m\u001b[32mIn[18]\u001b[39m\u001b[32m, line 12\u001b[39m\n\u001b[31m \u001b[39m\u001b[31mnorms = np.linalg.norm(x, axis=1, keepdims=True)agent_graph\u001b[39m\n ^\n\u001b[31mSyntaxError\u001b[39m\u001b[31m:\u001b[39m invalid syntax\n" ] } ], "source": [ "from dataclasses import dataclass\n", "from typing import Any, Iterable\n", " \n", "import numpy as np\n", " \n", "import mteb\n", "from mteb.types import Array\n", "from mteb.models import SearchEncoderWrapper\n", " \n", " \n", "def _l2_normalize(x: np.ndarray, eps: float = 1e-12) -> np.ndarray:\n", " norms = np.linalg.norm(x, axis=1, keepdims=True)agent_graph\n", " return x / np.clip(norms, eps, None)\n", " \n", " \n", "def _to_text_list(batch: dict[str, Any]) -> list[str]:\n", " \"\"\"\n", " MTEB batched inputs can be:\n", " - TextInput: {\"text\": [..]}\n", " - CorpusInput: {\"title\": [..], \"body\": [..], \"text\": [..]}\n", " - QueryInput: {\"query\": [..], \"instruction\": [..], \"text\": [..]}\n", " We prefer \"text\" if present; otherwise compose from title/body or query/instruction.\n", " \"\"\"\n", " if \"text\" in batch and batch[\"text\"] is not None:\n", " return list(batch[\"text\"])\n", " \n", " if \"title\" in batch and \"body\" in batch:\n", " titles = batch[\"title\"] or [\"\"] * len(batch[\"body\"])\n", " bodies = batch[\"body\"] or [\"\"] * len(batch[\"title\"])\n", " return [f\"{t} {b}\".strip() for t, b in zip(titles, bodies)]\n", " \n", " if \"query\" in batch:\n", " queries = list(batch[\"query\"])\n", " instructions = batch.get(\"instruction\")\n", " if instructions:\n", " return [f\"{i} {q}\".strip() for q, i in zip(queries, instructions)]\n", " return queries\n", " \n", " raise ValueError(f\"Unsupported batch keys: {sorted(batch.keys())}\")\n", " \n", " \n", "@dataclass\n", "class OllamaLangChainEncoder:\n", " lc_embeddings: Any # OllamaEmbeddings implements embed_documents()\n", " normalize: bool = True\n", " \n", " # Optional metadata hook used by some wrappers; safe to keep as None for local runs\n", " mteb_model_meta: Any = None\n", " \n", " def encode(\n", " self,\n", " inputs: Iterable[dict[str, Any]],\n", " *,\n", " task_metadata: Any,\n", " hf_split: str,\n", " hf_subset: str,\n", " prompt_type: Any = None,\n", " **kwargs: Any,\n", " ) -> Array:\n", " all_vecs: list[np.ndarray] = []\n", " \n", " for batch in inputs:\n", " texts = _to_text_list(batch)\n", " vecs = self.lc_embeddings.embed_documents(texts)\n", " arr = np.asarray(vecs, dtype=np.float32)\n", " if self.normalize:\n", " arr = _l2_normalize(arr)\n", " all_vecs.append(arr)\n", " \n", " if not all_vecs:\n", " return np.zeros((0, 0), dtype=np.float32)\n", " \n", " return np.vstack(all_vecs)\n", " \n", " def similarity(self, embeddings1: Array, embeddings2: Array) -> Array:\n", " a = np.asarray(embeddings1, dtype=np.float32)\n", " b = np.asarray(embeddings2, dtype=np.float32)\n", " if self.normalize:\n", " # dot == cosine if already normalized\n", " return a @ b.T\n", " a = _l2_normalize(a)\n", " b = _l2_normalize(b)\n", " return a @ b.T\n", " \n", " def similarity_pairwise(self, embeddings1: Array, embeddings2: Array) -> Array:\n", " a = np.asarray(embeddings1, dtype=np.float32)\n", " b = np.asarray(embeddings2, dtype=np.float32)\n", " if not self.normalize:\n", " a = _l2_normalize(a)\n", " b = _l2_normalize(b)\n", " return np.sum(a * b, axis=1)" ] }, { "cell_type": "code", "execution_count": null, "id": "85727a68", "metadata": {}, "outputs": [], "source": [ "encoder = OllamaLangChainEncoder(lc_embeddings=embeddings, normalize=True)\n", "search_model = SearchEncoderWrapper(encoder)\n", " \n", "tasks = mteb.get_tasks([\n", " \"CodeSearchNetRetrieval\",\n", " \"CodeSearchNetCCRetrieval\",\n", " \"AppsRetrieval\",\n", " \"StackOverflowDupQuestions\",\n", "])\n", "results = mteb.evaluate(\n", " model=search_model,\n", " tasks=tasks,\n", " encode_kwargs={\"batch_size\": 32, \"show_progress_bar\": True}\n", ")\n", " \n", "print(results)" ] }, { "cell_type": "code", "execution_count": null, "id": "4052f229", "metadata": {}, "outputs": [], "source": [] } ], "metadata": { "kernelspec": { "display_name": "assistance-engine", "language": "python", "name": "python3" }, "language_info": { "codemirror_mode": { "name": "ipython", "version": 3 }, "file_extension": ".py", "mimetype": "text/x-python", "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", "version": "3.11.13" } }, "nbformat": 4, "nbformat_minor": 5 }