diff --git a/gui/workflow_backend/Dockerfile b/gui/workflow_backend/Dockerfile index 97a79606..45d580f3 100644 --- a/gui/workflow_backend/Dockerfile +++ b/gui/workflow_backend/Dockerfile @@ -14,21 +14,19 @@ RUN apt-get update && apt-get install -y \ && rm -rf /var/lib/apt/lists/* # Install Poetry -RUN pip install poetry==1.7.1 +RUN pip install --no-cache-dir poetry==1.8.5 # Setting up Poetry (without creating a virtual environment) RUN poetry config virtualenvs.create false # Copy dependency files -COPY pyproject.toml poetry.lock* ./ +COPY pyproject.toml poetry.lock ./ -# Install base dependencies with poetry (core Django packages) -RUN poetry install --no-interaction --no-ansi --no-root --only main 2>&1 || \ - (echo "Poetry install failed, installing with pip..." && \ - pip install --no-cache-dir django djangorestframework psycopg2-binary drf-yasg django-cors-headers gunicorn python-dotenv pyjwt cryptography) +# Fail the build loudly if poetry.lock is out of sync with pyproject.toml +RUN poetry check --lock -# Install additional packages for parameter metadata service and jupyter execution -RUN pip install --no-cache-dir openai allensdk requests fuzzywuzzy python-Levenshtein websockets httpx +# Install the locked dependencies +RUN poetry install --no-interaction --no-ansi --no-root --only main # Copy project file COPY . . diff --git a/gui/workflow_backend/django-project/codes/neuroworkflow/utils/mcp/key.json b/gui/workflow_backend/django-project/codes/neuroworkflow/utils/mcp/key.json deleted file mode 100644 index 3c951bcd..00000000 --- a/gui/workflow_backend/django-project/codes/neuroworkflow/utils/mcp/key.json +++ /dev/null @@ -1,6 +0,0 @@ -{ -"OPENAI_API_KEY":"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", -"PROVIDER_BASE":"", -"LLM_MODEL":"gpt-4o-mini", -"MCP_SERVER":"server.py" -} diff --git a/gui/workflow_backend/django-project/codes/neuroworkflow/utils/mcp/llm_auto_mcp.ipynb b/gui/workflow_backend/django-project/codes/neuroworkflow/utils/mcp/llm_auto_mcp.ipynb deleted file mode 100644 index 5047f9ca..00000000 --- a/gui/workflow_backend/django-project/codes/neuroworkflow/utils/mcp/llm_auto_mcp.ipynb +++ /dev/null @@ -1,375 +0,0 @@ -{ - "cells": [ - { - "cell_type": "code", - "execution_count": 1, - "id": "8b08266c-dc21-49ea-a5ca-3d607ed0f16d", - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Overwriting server.py\n" - ] - } - ], - "source": [ - "\n", - "%%writefile server.py\n", - "# server.py\n", - "import sys, logging\n", - "from mcp.server.fastmcp import FastMCP\n", - "\n", - "# set up logging to stderr and to a file\n", - "logger = logging.getLogger(\"mcp-server\")\n", - "logger.setLevel(logging.DEBUG)\n", - "fmt = logging.Formatter(\"%(asctime)s [%(levelname)s] %(message)s\")\n", - "\n", - "stderr_h = logging.StreamHandler(sys.stderr)\n", - "stderr_h.setFormatter(fmt)\n", - "file_h = logging.FileHandler(\"server.log\", encoding=\"utf-8\")\n", - "file_h.setFormatter(fmt)\n", - "\n", - "logger.handlers.clear()\n", - "logger.addHandler(stderr_h)\n", - "logger.addHandler(file_h)\n", - "\n", - "mcp = FastMCP(\"demo\")\n", - "\n", - "@mcp.tool()\n", - "def add(a: int, b: int) -> int:\n", - " \"\"\"\n", - " Add two integers and return the sum.\n", - "\n", - " When to use:\n", - " - Any arithmetic addition request, including multi-step math where a sum is needed.\n", - "\n", - " Constraints:\n", - " - Only integers. For floats, round first or ask the user to confirm.\n", - "\n", - " Examples:\n", - " add(a=2, b=40) -> 42\n", - " add(a=321, b=123) -> 444\n", - " \"\"\"\n", - " logger.debug(f\"add() called with a={a}, b={b}\")\n", - " return a + b\n", - "\n", - "@mcp.tool()\n", - "def echo(text: str) -> str:\n", - " \"\"\"\n", - " Return the same text that was passed in.\n", - "\n", - " When to use:\n", - " - The user asks to reflect, quote, or transform exactly without changes.\n", - "\n", - " Examples:\n", - " echo(text=\"MCP works\") -> \"MCP works\"\n", - " \"\"\"\n", - " logger.debug(f\"echo() called with text={text!r}\")\n", - " return text\n", - "\n", - "if __name__ == \"__main__\":\n", - " logger.debug(\"MCP server starting...\")\n", - " mcp.run(\"stdio\")\n", - "\n" - ] - }, - { - "cell_type": "code", - "execution_count": 2, - "id": "7dc7eaeb-daf5-44e3-aae2-71c8292344a3", - "metadata": {}, - "outputs": [], - "source": [ - "#pip install mcp openai" - ] - }, - { - "cell_type": "code", - "execution_count": 3, - "id": "eb2adf9f-34d8-4f52-ab04-4b60dfaf0dbb", - "metadata": {}, - "outputs": [], - "source": [ - "#pip install --upgrade pip" - ] - }, - { - "cell_type": "code", - "execution_count": 4, - "id": "b8e3861a-e782-4a31-a162-83f81956475f", - "metadata": {}, - "outputs": [], - "source": [ - "\n", - "import os, json, asyncio, sys\n", - "from typing import Dict, Any, List\n", - "from openai import OpenAI\n", - "\n", - "from mcp import ClientSession, StdioServerParameters\n", - "from mcp.client.stdio import stdio_client\n", - "\n", - "def json_read(filename: str):\n", - " if os.path.isfile(filename):\n", - " with open(filename, \"r\", encoding=\"utf-8\") as f:\n", - " data = json.load(f)\n", - " return data, True\n", - " return {}, False\n", - "\n", - "async def tail_file(path: str):\n", - " try:\n", - " with open(path, \"r\", encoding=\"utf-8\") as f:\n", - " f.seek(0, 2) # jump to end\n", - " while True:\n", - " line = f.readline()\n", - " if not line:\n", - " await asyncio.sleep(0.2)\n", - " continue\n", - " print(\"[SERVER]\", line.rstrip())\n", - " except FileNotFoundError:\n", - " # file may not exist yet on first run\n", - " for _ in range(25):\n", - " await asyncio.sleep(0.2)\n", - " try:\n", - " with open(path, \"r\", encoding=\"utf-8\") as f:\n", - " break\n", - " except FileNotFoundError:\n", - " pass\n", - " # try again recursively if it appeared\n", - " return await tail_file(path)\n", - "\n", - "async def build_system_prompt(session) -> str:\n", - " tools = (await session.list_tools()).tools\n", - " lines = [\n", - " \"You can call tools when useful. Prefer precise tool usage. If no tool is needed, answer directly.\",\n", - " \"\",\n", - " \"Available tools:\"\n", - " ]\n", - " for t in tools:\n", - " desc = (t.description or \"\").strip()\n", - " # grab the first paragraph only for brevity\n", - " first_para = desc.split(\"\\n\\n\", 1)[0]\n", - " lines.append(f\"- {t.name}: {first_para}\")\n", - " lines.append(\"\")\n", - " lines.append(\"Use tool parameters exactly as defined in the provided tool schemas.\")\n", - " return \"\\n\".join(lines)\n", - "\n", - "api_data, ok = json_read(\"key.json\")\n", - "if not ok:\n", - " raise FileNotFoundError(\"key.json not found. Please create it.\")\n", - "\n", - "PROVIDER_BASE = api_data.get(\"PROVIDER_BASE\", \"\").strip()\n", - "API_KEY = api_data.get(\"OPENAI_API_KEY\", \"\").strip()\n", - "MODEL = api_data.get(\"LLM_MODEL\", \"gpt-4o-mini\").strip()\n", - "SERVER_PATH = api_data.get(\"MCP_SERVER\", \"server.py\").strip()\n", - "\n", - "#SYSTEM_PROMPT = (\n", - "# \"You can call tools when useful. Use add for arithmetic, echo for reflecting text. \"\n", - "# \"If no tool is needed, answer directly.\"\n", - "#)\n", - "\n", - "def make_client() -> OpenAI:\n", - " # Always pass the API key from JSON to avoid relying on environment variables\n", - " if PROVIDER_BASE:\n", - " return OpenAI(base_url=PROVIDER_BASE, api_key=API_KEY)\n", - " return OpenAI(api_key=API_KEY)\n", - "\n", - "def schema_from_mcp_tool(t) -> Dict[str, Any]:\n", - " # Convert an MCP ToolDescriptor to OpenAI tool schema\n", - " params = getattr(t, \"inputSchema\", None) or getattr(t, \"input_schema\", None)\n", - " if not params:\n", - " params = {\"type\": \"object\", \"properties\": {}, \"additionalProperties\": False}\n", - " return {\n", - " \"type\": \"function\",\n", - " \"function\": {\n", - " \"name\": t.name,\n", - " \"description\": getattr(t, \"description\", \"\") or \"\",\n", - " \"parameters\": params,\n", - " },\n", - " }\n", - "\n", - "def extract_text_content(mcp_result) -> str:\n", - " parts = []\n", - " for c in getattr(mcp_result, \"content\", []) or []:\n", - " text = getattr(c, \"text\", None)\n", - " if text is not None:\n", - " parts.append(text)\n", - " return \"\\n\".join(parts) if parts else \"\"\n", - "\n", - "async def build_openai_tools(session: ClientSession) -> List[Dict[str, Any]]:\n", - " tools_resp = await session.list_tools()\n", - " return [schema_from_mcp_tool(t) for t in tools_resp.tools]\n", - "\n", - "async def run_chat_once(user_msg: str) -> str:\n", - " client = make_client()\n", - "\n", - " # Spawn the MCP server over stdio and open a session\n", - " params = StdioServerParameters(command=sys.executable, args=[SERVER_PATH])\n", - "\n", - " # start tail task before connecting\n", - " log_task = asyncio.create_task(tail_file(\"server.log\"))\n", - " \n", - " async with stdio_client(params) as (read_stream, write_stream):\n", - " async with ClientSession(read_stream, write_stream) as session:\n", - " await session.initialize()\n", - "\n", - " openai_tools = await build_openai_tools(session)\n", - "\n", - " print(openai_tools)\n", - "\n", - " #system_prompt\n", - " system_prompt = await build_system_prompt(session)\n", - " print(system_prompt)\n", - "\n", - " messages: List[Dict[str, Any]] = [\n", - " {\"role\": \"system\", \"content\": system_prompt},\n", - " {\"role\": \"user\", \"content\": user_msg},\n", - " ]\n", - "\n", - " for _ in range(5):\n", - " chat = client.chat.completions.create(\n", - " model=MODEL,\n", - " messages=messages,\n", - " tools=openai_tools,\n", - " tool_choice=\"auto\",\n", - " )\n", - " choice = chat.choices[0]\n", - " finish = choice.finish_reason\n", - "\n", - " if finish == \"tool_calls\" and choice.message.tool_calls:\n", - " tool_messages = []\n", - " for call in choice.message.tool_calls:\n", - " name = call.function.name\n", - " args = json.loads(call.function.arguments or \"{}\")\n", - " result = await session.call_tool(name, args)\n", - " tool_messages.append({\n", - " \"role\": \"tool\",\n", - " \"tool_call_id\": call.id,\n", - " \"name\": name,\n", - " \"content\": extract_text_content(result),\n", - " })\n", - " messages = messages + [choice.message] + tool_messages\n", - " continue\n", - "\n", - " return choice.message.content or \"\"\n", - "\n", - " # stop the tailer\n", - " log_task.cancel()\n", - " try:\n", - " await log_task\n", - " except asyncio.CancelledError:\n", - " pass\n", - " \n", - " return \"\"\n", - "\n", - "# Notebook-friendly entry point\n", - "def run_demo(prompt: str = \"Please add 123 and 456 using the tool.\"):\n", - " # Jupyter often has an existing event loop. Use nest_asyncio to allow nested runs.\n", - " import nest_asyncio, asyncio\n", - " nest_asyncio.apply()\n", - " try:\n", - " loop = asyncio.get_event_loop()\n", - " return loop.run_until_complete(run_chat_once(prompt))\n", - " except RuntimeError:\n", - " # Fallback: create a new loop\n", - " return asyncio.run(run_chat_once(prompt))\n" - ] - }, - { - "cell_type": "code", - "execution_count": 5, - "id": "613e41a0-966d-4f21-89a4-a9a8d206bb3e", - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "[{'type': 'function', 'function': {'name': 'add', 'description': '\\n Add two integers and return the sum.\\n\\n When to use:\\n - Any arithmetic addition request, including multi-step math where a sum is needed.\\n\\n Constraints:\\n - Only integers. For floats, round first or ask the user to confirm.\\n\\n Examples:\\n add(a=2, b=40) -> 42\\n add(a=321, b=123) -> 444\\n ', 'parameters': {'properties': {'a': {'title': 'A', 'type': 'integer'}, 'b': {'title': 'B', 'type': 'integer'}}, 'required': ['a', 'b'], 'title': 'addArguments', 'type': 'object'}}}, {'type': 'function', 'function': {'name': 'echo', 'description': '\\n Return the same text that was passed in.\\n\\n When to use:\\n - The user asks to reflect, quote, or transform exactly without changes.\\n\\n Examples:\\n echo(text=\"MCP works\") -> \"MCP works\"\\n ', 'parameters': {'properties': {'text': {'title': 'Text', 'type': 'string'}}, 'required': ['text'], 'title': 'echoArguments', 'type': 'object'}}}]\n", - "You can call tools when useful. Prefer precise tool usage. If no tool is needed, answer directly.\n", - "\n", - "Available tools:\n", - "- add: Add two integers and return the sum.\n", - "- echo: Return the same text that was passed in.\n", - "\n", - "Use tool parameters exactly as defined in the provided tool schemas.\n", - "[SERVER] 2025-09-29 16:07:18,031 [DEBUG] MCP server starting...\n", - "[SERVER] 2025-09-29 16:07:21,109 [DEBUG] add() called with a=321, b=123\n", - "[SERVER] 2025-09-29 16:07:21,116 [DEBUG] echo() called with text='MCP works'\n", - "The sum of 321 and 123 is 444. \n", - "\n", - "Also, 'MCP works'.\n", - "[SERVER] 2025-09-29 16:07:23,208 [DEBUG] MCP server starting...\n", - "[SERVER] 2025-09-29 16:07:24,459 [DEBUG] echo() called with text='MCP works'\n" - ] - } - ], - "source": [ - "import os\n", - "os.environ[\"OPENAI_LOG\"] = \"\" \n", - "\n", - "print(run_demo(\"Add 321 and 123 using the tool, and then echo 'MCP works'.\"))\n" - ] - }, - { - "cell_type": "code", - "execution_count": 6, - "id": "a3845b97-15bb-4407-b511-c54e59d19abc", - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "[{'type': 'function', 'function': {'name': 'add', 'description': '\\n Add two integers and return the sum.\\n\\n When to use:\\n - Any arithmetic addition request, including multi-step math where a sum is needed.\\n\\n Constraints:\\n - Only integers. For floats, round first or ask the user to confirm.\\n\\n Examples:\\n add(a=2, b=40) -> 42\\n add(a=321, b=123) -> 444\\n ', 'parameters': {'properties': {'a': {'title': 'A', 'type': 'integer'}, 'b': {'title': 'B', 'type': 'integer'}}, 'required': ['a', 'b'], 'title': 'addArguments', 'type': 'object'}}}, {'type': 'function', 'function': {'name': 'echo', 'description': '\\n Return the same text that was passed in.\\n\\n When to use:\\n - The user asks to reflect, quote, or transform exactly without changes.\\n\\n Examples:\\n echo(text=\"MCP works\") -> \"MCP works\"\\n ', 'parameters': {'properties': {'text': {'title': 'Text', 'type': 'string'}}, 'required': ['text'], 'title': 'echoArguments', 'type': 'object'}}}]\n", - "You can call tools when useful. Prefer precise tool usage. If no tool is needed, answer directly.\n", - "\n", - "Available tools:\n", - "- add: Add two integers and return the sum.\n", - "- echo: Return the same text that was passed in.\n", - "\n", - "Use tool parameters exactly as defined in the provided tool schemas.\n", - "[SERVER] 2025-09-29 16:07:23,208 [DEBUG] MCP server starting...\n", - "[SERVER] 2025-09-29 16:07:24,459 [DEBUG] echo() called with text='MCP works'\n", - "The result of 321 divided by 123 is approximately 2.607. Since division cannot be performed with the available tools, I provided the closest approximation.\n", - "\n", - "Additionally, here is the echoed text: \"MCP works\".\n" - ] - } - ], - "source": [ - "print(run_demo(\"devide 321 by 123 using the tool, and then echo 'MCP works'.\"))\n" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "98c9afab-f514-40f5-a6c4-0311ba4a537b", - "metadata": {}, - "outputs": [], - "source": [] - } - ], - "metadata": { - "kernelspec": { - "display_name": "Python 3 (ipykernel)", - "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.12.10" - } - }, - "nbformat": 4, - "nbformat_minor": 5 -} diff --git a/gui/workflow_backend/django-project/codes/neuroworkflow/utils/mcp/server.log b/gui/workflow_backend/django-project/codes/neuroworkflow/utils/mcp/server.log deleted file mode 100644 index 2f65034b..00000000 --- a/gui/workflow_backend/django-project/codes/neuroworkflow/utils/mcp/server.log +++ /dev/null @@ -1,11 +0,0 @@ -2025-08-16 17:08:26,401 [DEBUG] MCP server starting... -2025-08-16 17:08:28,538 [DEBUG] add() called with a=321, b=123 -2025-08-16 17:08:28,548 [DEBUG] echo() called with text='MCP works' -2025-08-16 17:25:53,624 [DEBUG] MCP server starting... -2025-08-16 17:25:55,662 [DEBUG] add() called with a=321, b=-123 -2025-08-16 17:25:55,672 [DEBUG] echo() called with text='MCP works' -2025-09-29 16:07:18,031 [DEBUG] MCP server starting... -2025-09-29 16:07:21,109 [DEBUG] add() called with a=321, b=123 -2025-09-29 16:07:21,116 [DEBUG] echo() called with text='MCP works' -2025-09-29 16:07:23,208 [DEBUG] MCP server starting... -2025-09-29 16:07:24,459 [DEBUG] echo() called with text='MCP works' diff --git a/gui/workflow_backend/django-project/codes/neuroworkflow/utils/mcp/server.py b/gui/workflow_backend/django-project/codes/neuroworkflow/utils/mcp/server.py deleted file mode 100644 index 7cd68a78..00000000 --- a/gui/workflow_backend/django-project/codes/neuroworkflow/utils/mcp/server.py +++ /dev/null @@ -1,56 +0,0 @@ -# server.py -import sys, logging -from mcp.server.fastmcp import FastMCP - -# set up logging to stderr and to a file -logger = logging.getLogger("mcp-server") -logger.setLevel(logging.DEBUG) -fmt = logging.Formatter("%(asctime)s [%(levelname)s] %(message)s") - -stderr_h = logging.StreamHandler(sys.stderr) -stderr_h.setFormatter(fmt) -file_h = logging.FileHandler("server.log", encoding="utf-8") -file_h.setFormatter(fmt) - -logger.handlers.clear() -logger.addHandler(stderr_h) -logger.addHandler(file_h) - -mcp = FastMCP("demo") - -@mcp.tool() -def add(a: int, b: int) -> int: - """ - Add two integers and return the sum. - - When to use: - - Any arithmetic addition request, including multi-step math where a sum is needed. - - Constraints: - - Only integers. For floats, round first or ask the user to confirm. - - Examples: - add(a=2, b=40) -> 42 - add(a=321, b=123) -> 444 - """ - logger.debug(f"add() called with a={a}, b={b}") - return a + b - -@mcp.tool() -def echo(text: str) -> str: - """ - Return the same text that was passed in. - - When to use: - - The user asks to reflect, quote, or transform exactly without changes. - - Examples: - echo(text="MCP works") -> "MCP works" - """ - logger.debug(f"echo() called with text={text!r}") - return text - -if __name__ == "__main__": - logger.debug("MCP server starting...") - mcp.run("stdio") - diff --git a/gui/workflow_backend/poetry.lock b/gui/workflow_backend/poetry.lock index cce00eb1..e0a41dd5 100644 --- a/gui/workflow_backend/poetry.lock +++ b/gui/workflow_backend/poetry.lock @@ -1,4 +1,4 @@ -# This file is automatically @generated by Poetry 1.7.1 and should not be changed by hand. +# This file is automatically @generated by Poetry 1.8.5 and should not be changed by hand. [[package]] name = "anyio" @@ -166,6 +166,144 @@ files = [ [package.dependencies] pycparser = "*" +[[package]] +name = "charset-normalizer" +version = "3.4.7" +description = "The Real First Universal Charset Detector. Open, modern and actively maintained alternative to Chardet." +optional = false +python-versions = ">=3.7" +files = [ + {file = "charset_normalizer-3.4.7-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:cdd68a1fb318e290a2077696b7eb7a21a49163c455979c639bf5a5dcdc46617d"}, + {file = "charset_normalizer-3.4.7-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:e17b8d5d6a8c47c85e68ca8379def1303fd360c3e22093a807cd34a71cd082b8"}, + {file = "charset_normalizer-3.4.7-cp310-cp310-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:511ef87c8aec0783e08ac18565a16d435372bc1ac25a91e6ac7f5ef2b0bff790"}, + {file = "charset_normalizer-3.4.7-cp310-cp310-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:007d05ec7321d12a40227aae9e2bc6dca73f3cb21058999a1df9e193555a9dcc"}, + {file = "charset_normalizer-3.4.7-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:cf29836da5119f3c8a8a70667b0ef5fdca3bb12f80fd06487cfa575b3909b393"}, + {file = "charset_normalizer-3.4.7-cp310-cp310-manylinux_2_31_armv7l.whl", hash = "sha256:12d8baf840cc7889b37c7c770f478adea7adce3dcb3944d02ec87508e2dcf153"}, + {file = "charset_normalizer-3.4.7-cp310-cp310-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:d560742f3c0d62afaccf9f41fe485ed69bd7661a241f86a3ef0f0fb8b1a397af"}, + {file = "charset_normalizer-3.4.7-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:b14b2d9dac08e28bb8046a1a0434b1750eb221c8f5b87a68f4fa11a6f97b5e34"}, + {file = "charset_normalizer-3.4.7-cp310-cp310-musllinux_1_2_armv7l.whl", hash = "sha256:bc17a677b21b3502a21f66a8cc64f5bfad4df8a0b8434d661666f8ce90ac3af1"}, + {file = "charset_normalizer-3.4.7-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:750e02e074872a3fad7f233b47734166440af3cdea0add3e95163110816d6752"}, + {file = "charset_normalizer-3.4.7-cp310-cp310-musllinux_1_2_riscv64.whl", hash = "sha256:4e5163c14bffd570ef2affbfdd77bba66383890797df43dc8b4cc7d6f500bf53"}, + {file = "charset_normalizer-3.4.7-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:6ed74185b2db44f41ef35fd1617c5888e59792da9bbc9190d6c7300617182616"}, + {file = "charset_normalizer-3.4.7-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:94e1885b270625a9a828c9793b4d52a64445299baa1fea5a173bf1d3dd9a1a5a"}, + {file = "charset_normalizer-3.4.7-cp310-cp310-win32.whl", hash = "sha256:6785f414ae0f3c733c437e0f3929197934f526d19dfaa75e18fdb4f94c6fb374"}, + {file = "charset_normalizer-3.4.7-cp310-cp310-win_amd64.whl", hash = "sha256:6696b7688f54f5af4462118f0bfa7c1621eeb87154f77fa04b9295ce7a8f2943"}, + {file = "charset_normalizer-3.4.7-cp310-cp310-win_arm64.whl", hash = "sha256:66671f93accb62ed07da56613636f3641f1a12c13046ce91ffc923721f23c008"}, + {file = "charset_normalizer-3.4.7-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:7641bb8895e77f921102f72833904dcd9901df5d6d72a2ab8f31d04b7e51e4e7"}, + {file = "charset_normalizer-3.4.7-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:202389074300232baeb53ae2569a60901f7efadd4245cf3a3bf0617d60b439d7"}, + {file = "charset_normalizer-3.4.7-cp311-cp311-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:30b8d1d8c52a48c2c5690e152c169b673487a2a58de1ec7393196753063fcd5e"}, + {file = "charset_normalizer-3.4.7-cp311-cp311-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:532bc9bf33a68613fd7d65e4b1c71a6a38d7d42604ecf239c77392e9b4e8998c"}, + {file = "charset_normalizer-3.4.7-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:2fe249cb4651fd12605b7288b24751d8bfd46d35f12a20b1ba33dea122e690df"}, + {file = "charset_normalizer-3.4.7-cp311-cp311-manylinux_2_31_armv7l.whl", hash = "sha256:65bcd23054beab4d166035cabbc868a09c1a49d1efe458fe8e4361215df40265"}, + {file = "charset_normalizer-3.4.7-cp311-cp311-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:08e721811161356f97b4059a9ba7bafb23ea5ee2255402c42881c214e173c6b4"}, + {file = "charset_normalizer-3.4.7-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:e060d01aec0a910bdccb8be71faf34e7799ce36950f8294c8bf612cba65a2c9e"}, + {file = "charset_normalizer-3.4.7-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:38c0109396c4cfc574d502df99742a45c72c08eff0a36158b6f04000043dbf38"}, + {file = "charset_normalizer-3.4.7-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:1c2a768fdd44ee4a9339a9b0b130049139b8ce3c01d2ce09f67f5a68048d477c"}, + {file = "charset_normalizer-3.4.7-cp311-cp311-musllinux_1_2_riscv64.whl", hash = "sha256:1a87ca9d5df6fe460483d9a5bbf2b18f620cbed41b432e2bddb686228282d10b"}, + {file = "charset_normalizer-3.4.7-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:d635aab80466bc95771bb78d5370e74d36d1fe31467b6b29b8b57b2a3cd7d22c"}, + {file = "charset_normalizer-3.4.7-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:ae196f021b5e7c78e918242d217db021ed2a6ace2bc6ae94c0fc596221c7f58d"}, + {file = "charset_normalizer-3.4.7-cp311-cp311-win32.whl", hash = "sha256:adb2597b428735679446b46c8badf467b4ca5f5056aae4d51a19f9570301b1ad"}, + {file = "charset_normalizer-3.4.7-cp311-cp311-win_amd64.whl", hash = "sha256:8e385e4267ab76874ae30db04c627faaaf0b509e1ccc11a95b3fc3e83f855c00"}, + {file = "charset_normalizer-3.4.7-cp311-cp311-win_arm64.whl", hash = "sha256:d4a48e5b3c2a489fae013b7589308a40146ee081f6f509e047e0e096084ceca1"}, + {file = "charset_normalizer-3.4.7-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:eca9705049ad3c7345d574e3510665cb2cf844c2f2dcfe675332677f081cbd46"}, + {file = "charset_normalizer-3.4.7-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6178f72c5508bfc5fd446a5905e698c6212932f25bcdd4b47a757a50605a90e2"}, + {file = "charset_normalizer-3.4.7-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:e1421b502d83040e6d7fb2fb18dff63957f720da3d77b2fbd3187ceb63755d7b"}, + {file = "charset_normalizer-3.4.7-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:edac0f1ab77644605be2cbba52e6b7f630731fc42b34cb0f634be1a6eface56a"}, + {file = "charset_normalizer-3.4.7-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:5649fd1c7bade02f320a462fdefd0b4bd3ce036065836d4f42e0de958038e116"}, + {file = "charset_normalizer-3.4.7-cp312-cp312-manylinux_2_31_armv7l.whl", hash = "sha256:203104ed3e428044fd943bc4bf45fa73c0730391f9621e37fe39ecf477b128cb"}, + {file = "charset_normalizer-3.4.7-cp312-cp312-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:298930cec56029e05497a76988377cbd7457ba864beeea92ad7e844fe74cd1f1"}, + {file = "charset_normalizer-3.4.7-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:708838739abf24b2ceb208d0e22403dd018faeef86ddac04319a62ae884c4f15"}, + {file = "charset_normalizer-3.4.7-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:0f7eb884681e3938906ed0434f20c63046eacd0111c4ba96f27b76084cd679f5"}, + {file = "charset_normalizer-3.4.7-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:4dc1e73c36828f982bfe79fadf5919923f8a6f4df2860804db9a98c48824ce8d"}, + {file = "charset_normalizer-3.4.7-cp312-cp312-musllinux_1_2_riscv64.whl", hash = "sha256:aed52fea0513bac0ccde438c188c8a471c4e0f457c2dd20cdbf6ea7a450046c7"}, + {file = "charset_normalizer-3.4.7-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:fea24543955a6a729c45a73fe90e08c743f0b3334bbf3201e6c4bc1b0c7fa464"}, + {file = "charset_normalizer-3.4.7-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:bb6d88045545b26da47aa879dd4a89a71d1dce0f0e549b1abcb31dfe4a8eac49"}, + {file = "charset_normalizer-3.4.7-cp312-cp312-win32.whl", hash = "sha256:2257141f39fe65a3fdf38aeccae4b953e5f3b3324f4ff0daf9f15b8518666a2c"}, + {file = "charset_normalizer-3.4.7-cp312-cp312-win_amd64.whl", hash = "sha256:5ed6ab538499c8644b8a3e18debabcd7ce684f3fa91cf867521a7a0279cab2d6"}, + {file = "charset_normalizer-3.4.7-cp312-cp312-win_arm64.whl", hash = "sha256:56be790f86bfb2c98fb742ce566dfb4816e5a83384616ab59c49e0604d49c51d"}, + {file = "charset_normalizer-3.4.7-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:f496c9c3cc02230093d8330875c4c3cdfc3b73612a5fd921c65d39cbcef08063"}, + {file = "charset_normalizer-3.4.7-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:0ea948db76d31190bf08bd371623927ee1339d5f2a0b4b1b4a4439a65298703c"}, + {file = "charset_normalizer-3.4.7-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:a277ab8928b9f299723bc1a2dabb1265911b1a76341f90a510368ca44ad9ab66"}, + {file = "charset_normalizer-3.4.7-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:3bec022aec2c514d9cf199522a802bd007cd588ab17ab2525f20f9c34d067c18"}, + {file = "charset_normalizer-3.4.7-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:e044c39e41b92c845bc815e5ae4230804e8e7bc29e399b0437d64222d92809dd"}, + {file = "charset_normalizer-3.4.7-cp313-cp313-manylinux_2_31_armv7l.whl", hash = "sha256:f495a1652cf3fbab2eb0639776dad966c2fb874d79d87ca07f9d5f059b8bd215"}, + {file = "charset_normalizer-3.4.7-cp313-cp313-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:e712b419df8ba5e42b226c510472b37bd57b38e897d3eca5e8cfd410a29fa859"}, + {file = "charset_normalizer-3.4.7-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:7804338df6fcc08105c7745f1502ba68d900f45fd770d5bdd5288ddccb8a42d8"}, + {file = "charset_normalizer-3.4.7-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:481551899c856c704d58119b5025793fa6730adda3571971af568f66d2424bb5"}, + {file = "charset_normalizer-3.4.7-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:f59099f9b66f0d7145115e6f80dd8b1d847176df89b234a5a6b3f00437aa0832"}, + {file = "charset_normalizer-3.4.7-cp313-cp313-musllinux_1_2_riscv64.whl", hash = "sha256:f59ad4c0e8f6bba240a9bb85504faa1ab438237199d4cce5f622761507b8f6a6"}, + {file = "charset_normalizer-3.4.7-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:3dedcc22d73ec993f42055eff4fcfed9318d1eeb9a6606c55892a26964964e48"}, + {file = "charset_normalizer-3.4.7-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:64f02c6841d7d83f832cd97ccf8eb8a906d06eb95d5276069175c696b024b60a"}, + {file = "charset_normalizer-3.4.7-cp313-cp313-win32.whl", hash = "sha256:4042d5c8f957e15221d423ba781e85d553722fc4113f523f2feb7b188cc34c5e"}, + {file = "charset_normalizer-3.4.7-cp313-cp313-win_amd64.whl", hash = "sha256:3946fa46a0cf3e4c8cb1cc52f56bb536310d34f25f01ca9b6c16afa767dab110"}, + {file = "charset_normalizer-3.4.7-cp313-cp313-win_arm64.whl", hash = "sha256:80d04837f55fc81da168b98de4f4b797ef007fc8a79ab71c6ec9bc4dd662b15b"}, + {file = "charset_normalizer-3.4.7-cp314-cp314-macosx_10_15_universal2.whl", hash = "sha256:c36c333c39be2dbca264d7803333c896ab8fa7d4d6f0ab7edb7dfd7aea6e98c0"}, + {file = "charset_normalizer-3.4.7-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:1c2aed2e5e41f24ea8ef1590b8e848a79b56f3a5564a65ceec43c9d692dc7d8a"}, + {file = "charset_normalizer-3.4.7-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:54523e136b8948060c0fa0bc7b1b50c32c186f2fceee897a495406bb6e311d2b"}, + {file = "charset_normalizer-3.4.7-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:715479b9a2802ecac752a3b0efa2b0b60285cf962ee38414211abdfccc233b41"}, + {file = "charset_normalizer-3.4.7-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:bd6c2a1c7573c64738d716488d2cdd3c00e340e4835707d8fdb8dc1a66ef164e"}, + {file = "charset_normalizer-3.4.7-cp314-cp314-manylinux_2_31_armv7l.whl", hash = "sha256:c45e9440fb78f8ddabcf714b68f936737a121355bf59f3907f4e17721b9d1aae"}, + {file = "charset_normalizer-3.4.7-cp314-cp314-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:3534e7dcbdcf757da6b85a0bbf5b6868786d5982dd959b065e65481644817a18"}, + {file = "charset_normalizer-3.4.7-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:e8ac484bf18ce6975760921bb6148041faa8fef0547200386ea0b52b5d27bf7b"}, + {file = "charset_normalizer-3.4.7-cp314-cp314-musllinux_1_2_armv7l.whl", hash = "sha256:a5fe03b42827c13cdccd08e6c0247b6a6d4b5e3cdc53fd1749f5896adcdc2356"}, + {file = "charset_normalizer-3.4.7-cp314-cp314-musllinux_1_2_ppc64le.whl", hash = "sha256:2d6eb928e13016cea4f1f21d1e10c1cebd5a421bc57ddf5b1142ae3f86824fab"}, + {file = "charset_normalizer-3.4.7-cp314-cp314-musllinux_1_2_riscv64.whl", hash = "sha256:e74327fb75de8986940def6e8dee4f127cc9752bee7355bb323cc5b2659b6d46"}, + {file = "charset_normalizer-3.4.7-cp314-cp314-musllinux_1_2_s390x.whl", hash = "sha256:d6038d37043bced98a66e68d3aa2b6a35505dc01328cd65217cefe82f25def44"}, + {file = "charset_normalizer-3.4.7-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:7579e913a5339fb8fa133f6bbcfd8e6749696206cf05acdbdca71a1b436d8e72"}, + {file = "charset_normalizer-3.4.7-cp314-cp314-win32.whl", hash = "sha256:5b77459df20e08151cd6f8b9ef8ef1f961ef73d85c21a555c7eed5b79410ec10"}, + {file = "charset_normalizer-3.4.7-cp314-cp314-win_amd64.whl", hash = "sha256:92a0a01ead5e668468e952e4238cccd7c537364eb7d851ab144ab6627dbbe12f"}, + {file = "charset_normalizer-3.4.7-cp314-cp314-win_arm64.whl", hash = "sha256:67f6279d125ca0046a7fd386d01b311c6363844deac3e5b069b514ba3e63c246"}, + {file = "charset_normalizer-3.4.7-cp314-cp314t-macosx_10_15_universal2.whl", hash = "sha256:effc3f449787117233702311a1b7d8f59cba9ced946ba727bdc329ec69028e24"}, + {file = "charset_normalizer-3.4.7-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:fbccdc05410c9ee21bbf16a35f4c1d16123dcdeb8a1d38f33654fa21d0234f79"}, + {file = "charset_normalizer-3.4.7-cp314-cp314t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:733784b6d6def852c814bce5f318d25da2ee65dd4839a0718641c696e09a2960"}, + {file = "charset_normalizer-3.4.7-cp314-cp314t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:a89c23ef8d2c6b27fd200a42aa4ac72786e7c60d40efdc76e6011260b6e949c4"}, + {file = "charset_normalizer-3.4.7-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:6c114670c45346afedc0d947faf3c7f701051d2518b943679c8ff88befe14f8e"}, + {file = "charset_normalizer-3.4.7-cp314-cp314t-manylinux_2_31_armv7l.whl", hash = "sha256:a180c5e59792af262bf263b21a3c49353f25945d8d9f70628e73de370d55e1e1"}, + {file = "charset_normalizer-3.4.7-cp314-cp314t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:3c9a494bc5ec77d43cea229c4f6db1e4d8fe7e1bbffa8b6f0f0032430ff8ab44"}, + {file = "charset_normalizer-3.4.7-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:8d828b6667a32a728a1ad1d93957cdf37489c57b97ae6c4de2860fa749b8fc1e"}, + {file = "charset_normalizer-3.4.7-cp314-cp314t-musllinux_1_2_armv7l.whl", hash = "sha256:cf1493cd8607bec4d8a7b9b004e699fcf8f9103a9284cc94962cb73d20f9d4a3"}, + {file = "charset_normalizer-3.4.7-cp314-cp314t-musllinux_1_2_ppc64le.whl", hash = "sha256:0c96c3b819b5c3e9e165495db84d41914d6894d55181d2d108cc1a69bfc9cce0"}, + {file = "charset_normalizer-3.4.7-cp314-cp314t-musllinux_1_2_riscv64.whl", hash = "sha256:752a45dc4a6934060b3b0dab47e04edc3326575f82be64bc4fc293914566503e"}, + {file = "charset_normalizer-3.4.7-cp314-cp314t-musllinux_1_2_s390x.whl", hash = "sha256:8778f0c7a52e56f75d12dae53ae320fae900a8b9b4164b981b9c5ce059cd1fcb"}, + {file = "charset_normalizer-3.4.7-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:ce3412fbe1e31eb81ea42f4169ed94861c56e643189e1e75f0041f3fe7020abe"}, + {file = "charset_normalizer-3.4.7-cp314-cp314t-win32.whl", hash = "sha256:c03a41a8784091e67a39648f70c5f97b5b6a37f216896d44d2cdcb82615339a0"}, + {file = "charset_normalizer-3.4.7-cp314-cp314t-win_amd64.whl", hash = "sha256:03853ed82eeebbce3c2abfdbc98c96dc205f32a79627688ac9a27370ea61a49c"}, + {file = "charset_normalizer-3.4.7-cp314-cp314t-win_arm64.whl", hash = "sha256:c35abb8bfff0185efac5878da64c45dafd2b37fb0383add1be155a763c1f083d"}, + {file = "charset_normalizer-3.4.7-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:e5f4d355f0a2b1a31bc3edec6795b46324349c9cb25eed068049e4f472fb4259"}, + {file = "charset_normalizer-3.4.7-cp38-cp38-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:16d971e29578a5e97d7117866d15889a4a07befe0e87e703ed63cd90cb348c01"}, + {file = "charset_normalizer-3.4.7-cp38-cp38-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:dca4bbc466a95ba9c0234ef56d7dd9509f63da22274589ebd4ed7f1f4d4c54e3"}, + {file = "charset_normalizer-3.4.7-cp38-cp38-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:e80c8378d8f3d83cd3164da1ad2df9e37a666cdde7b1cb2298ed0b558064be30"}, + {file = "charset_normalizer-3.4.7-cp38-cp38-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:36836d6ff945a00b88ba1e4572d721e60b5b8c98c155d465f56ad19d68f23734"}, + {file = "charset_normalizer-3.4.7-cp38-cp38-manylinux_2_31_armv7l.whl", hash = "sha256:bd9b23791fe793e4968dba0c447e12f78e425c59fc0e3b97f6450f4781f3ee60"}, + {file = "charset_normalizer-3.4.7-cp38-cp38-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:aef65cd602a6d0e0ff6f9930fcb1c8fec60dd2cfcb6facaf4bdb0e5873042db0"}, + {file = "charset_normalizer-3.4.7-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:82b271f5137d07749f7bf32f70b17ab6eaabedd297e75dce75081a24f76eb545"}, + {file = "charset_normalizer-3.4.7-cp38-cp38-musllinux_1_2_armv7l.whl", hash = "sha256:1efde3cae86c8c273f1eb3b287be7d8499420cf2fe7585c41d370d3e790054a5"}, + {file = "charset_normalizer-3.4.7-cp38-cp38-musllinux_1_2_ppc64le.whl", hash = "sha256:c593052c465475e64bbfe5dbd81680f64a67fdc752c56d7a0ae205dc8aeefe0f"}, + {file = "charset_normalizer-3.4.7-cp38-cp38-musllinux_1_2_riscv64.whl", hash = "sha256:af21eb4409a119e365397b2adbaca4c9ccab56543a65d5dbd9f920d6ac29f686"}, + {file = "charset_normalizer-3.4.7-cp38-cp38-musllinux_1_2_s390x.whl", hash = "sha256:84c018e49c3bf790f9c2771c45e9313a08c2c2a6342b162cd650258b57817706"}, + {file = "charset_normalizer-3.4.7-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:dd915403e231e6b1809fe9b6d9fc55cf8fb5e02765ac625d9cd623342a7905d7"}, + {file = "charset_normalizer-3.4.7-cp38-cp38-win32.whl", hash = "sha256:320ade88cfb846b8cd6b4ddf5ee9e80ee0c1f52401f2456b84ae1ae6a1a5f207"}, + {file = "charset_normalizer-3.4.7-cp38-cp38-win_amd64.whl", hash = "sha256:1dc8b0ea451d6e69735094606991f32867807881400f808a106ee1d963c46a83"}, + {file = "charset_normalizer-3.4.7-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:177a0ba5f0211d488e295aaf82707237e331c24788d8d76c96c5a41594723217"}, + {file = "charset_normalizer-3.4.7-cp39-cp39-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6e0d51f618228538a3e8f46bd246f87a6cd030565e015803691603f55e12afb5"}, + {file = "charset_normalizer-3.4.7-cp39-cp39-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:14265bfe1f09498b9d8ec91e9ec9fa52775edf90fcbde092b25f4a33d444fea9"}, + {file = "charset_normalizer-3.4.7-cp39-cp39-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:87fad7d9ba98c86bcb41b2dc8dbb326619be2562af1f8ff50776a39e55721c5a"}, + {file = "charset_normalizer-3.4.7-cp39-cp39-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:f22dec1690b584cea26fade98b2435c132c1b5f68e39f5a0b7627cd7ae31f1dc"}, + {file = "charset_normalizer-3.4.7-cp39-cp39-manylinux_2_31_armv7l.whl", hash = "sha256:d61f00a0869d77422d9b2aba989e2d24afa6ffd552af442e0e58de4f35ea6d00"}, + {file = "charset_normalizer-3.4.7-cp39-cp39-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:6370e8686f662e6a3941ee48ed4742317cafbe5707e36406e9df792cdb535776"}, + {file = "charset_normalizer-3.4.7-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:a6c5863edfbe888d9eff9c8b8087354e27618d9da76425c119293f11712a6319"}, + {file = "charset_normalizer-3.4.7-cp39-cp39-musllinux_1_2_armv7l.whl", hash = "sha256:ed065083d0898c9d5b4bbec7b026fd755ff7454e6e8b73a67f8c744b13986e24"}, + {file = "charset_normalizer-3.4.7-cp39-cp39-musllinux_1_2_ppc64le.whl", hash = "sha256:2cd4a60d0e2fb04537162c62bbbb4182f53541fe0ede35cdf270a1c1e723cc42"}, + {file = "charset_normalizer-3.4.7-cp39-cp39-musllinux_1_2_riscv64.whl", hash = "sha256:813c0e0132266c08eb87469a642cb30aaff57c5f426255419572aaeceeaa7bf4"}, + {file = "charset_normalizer-3.4.7-cp39-cp39-musllinux_1_2_s390x.whl", hash = "sha256:07d9e39b01743c3717745f4c530a6349eadbfa043c7577eef86c502c15df2c67"}, + {file = "charset_normalizer-3.4.7-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:c0f081d69a6e58272819b70288d3221a6ee64b98df852631c80f293514d3b274"}, + {file = "charset_normalizer-3.4.7-cp39-cp39-win32.whl", hash = "sha256:8751d2787c9131302398b11e6c8068053dcb55d5a8964e114b6e196cf16cb366"}, + {file = "charset_normalizer-3.4.7-cp39-cp39-win_amd64.whl", hash = "sha256:12a6fff75f6bc66711b73a2f0addfc4c8c15a20e805146a02d147a318962c444"}, + {file = "charset_normalizer-3.4.7-cp39-cp39-win_arm64.whl", hash = "sha256:bb8cc7534f51d9a017b93e3e85b260924f909601c3df002bcdb58ddb4dc41a5c"}, + {file = "charset_normalizer-3.4.7-py3-none-any.whl", hash = "sha256:3dce51d0f5e7951f8bb4900c257dad282f49190fdbebecd4ba99bcc41fef404d"}, + {file = "charset_normalizer-3.4.7.tar.gz", hash = "sha256:ae89db9e5f98a11a4bf50407d4363e7b09b31e55bc117b4f7d80aab97ba009e5"}, +] + [[package]] name = "click" version = "8.1.8" @@ -487,6 +625,87 @@ files = [ {file = "mypy_extensions-1.0.0.tar.gz", hash = "sha256:75dbf8955dc00442a438fc4d0666508a9a97b6bd41aa2f0ffe9d2f2725af0782"}, ] +[[package]] +name = "numpy" +version = "2.4.6" +description = "Fundamental package for array computing in Python" +optional = false +python-versions = ">=3.11" +files = [ + {file = "numpy-2.4.6-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:0280e0356c0829a18d9de1cb7eee50ec22ca639878d7240307ca0943d73cd2c4"}, + {file = "numpy-2.4.6-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:110f8b71aacb688ec69062bb7f6938a0f8acb01b7c1c4beb453c65b6d234584d"}, + {file = "numpy-2.4.6-cp311-cp311-macosx_14_0_arm64.whl", hash = "sha256:4cfe66903cc32a9921a6733d96b19bb6abf310397581bbad89c228f5abaf0ee8"}, + {file = "numpy-2.4.6-cp311-cp311-macosx_14_0_x86_64.whl", hash = "sha256:8155154c7c691289fe18f510b5d4657c68c67989f293f0535a91360392ff6538"}, + {file = "numpy-2.4.6-cp311-cp311-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:0ab0a9c4ffb1a6d95ef519fe4247dba8eb6b18ad93999f76b7f657039acabd47"}, + {file = "numpy-2.4.6-cp311-cp311-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:89cd468399cfd2504718f0ba50e410dca55a170b61a02ad92bb18c8a65186e93"}, + {file = "numpy-2.4.6-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:c2d37ab77531417474168eb79d6d80b14f821a966818505d03013d0833edb7a8"}, + {file = "numpy-2.4.6-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:f407cb6b8e9d6d8c626bc73c945db1706035af8fd632295547bf1c9e46d092d6"}, + {file = "numpy-2.4.6-cp311-cp311-win32.whl", hash = "sha256:ddea102b48f9e339f3948bf22040944184627a30fdf7f858667673b9c5f033c8"}, + {file = "numpy-2.4.6-cp311-cp311-win_amd64.whl", hash = "sha256:1e254a00cdf42b1e4d5b3d68d33af63268d41340d8885df2ab6470f2e1500147"}, + {file = "numpy-2.4.6-cp311-cp311-win_arm64.whl", hash = "sha256:ed9749eef4cbd126da3dc1d6bcb3a57f5eb7ac6a6484146bdbf743f552dfc577"}, + {file = "numpy-2.4.6-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:001fbb8e08d942dd57599e781f2472269ee7f2755fae407b4f67b2f0b17da3f1"}, + {file = "numpy-2.4.6-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:ebfb099f8dcf083deef3ac1ca4c1503f387cf76296fcb3816b66f5ecb5f54fdb"}, + {file = "numpy-2.4.6-cp312-cp312-macosx_14_0_arm64.whl", hash = "sha256:3213d622a0283a39a93d188f3cf72b26862df52fbb4ca3697f51705016523d41"}, + {file = "numpy-2.4.6-cp312-cp312-macosx_14_0_x86_64.whl", hash = "sha256:357cc07a6d7b0b182ff02249616a03742827ebb1277546b5c7cd7f7620a45698"}, + {file = "numpy-2.4.6-cp312-cp312-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:5f9fb9157b4ce2971008323afe46053787b526ef624fea915b261468a8421a0f"}, + {file = "numpy-2.4.6-cp312-cp312-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:90f9849678c75fe7afa2d348ac842c168b0a4d3d61919687216dfc547976d853"}, + {file = "numpy-2.4.6-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:c1a2af6c6ef86344a6b0db6b97834208bf598db514f2b155042439b62605601a"}, + {file = "numpy-2.4.6-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:e5805d5a22fd19c8ccff10a9561f9df94436b0545619ea579db2d3c35294bce2"}, + {file = "numpy-2.4.6-cp312-cp312-win32.whl", hash = "sha256:e3eeb0aabd6bd5ce64faae67e9935203a6991b4bc2a485a767fbafb2c5125f45"}, + {file = "numpy-2.4.6-cp312-cp312-win_amd64.whl", hash = "sha256:d8e8286dd7cea7895157318d1b91cdacac64c479f3cbc8dce548331728484751"}, + {file = "numpy-2.4.6-cp312-cp312-win_arm64.whl", hash = "sha256:4081eb135ac24158bd51cdfbef16f1c64df7063b1143f24731387137c092bec8"}, + {file = "numpy-2.4.6-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:511dbaf848decaaaf4b4ca48032619fb3138710c4bf7da7617765edad1ef96b0"}, + {file = "numpy-2.4.6-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:bf162abab1c1a736333192707cef898e735a5ca00f38f27eeedf44b39d9e85eb"}, + {file = "numpy-2.4.6-cp313-cp313-macosx_14_0_arm64.whl", hash = "sha256:043191bfa8eab18c776647b62723ac9dddece59743b13f49b2016094129c2b3f"}, + {file = "numpy-2.4.6-cp313-cp313-macosx_14_0_x86_64.whl", hash = "sha256:6180d8b35af935aed8ece3a85e0a43f87393ae0ac87c8d2c8bd2c993f7270ef3"}, + {file = "numpy-2.4.6-cp313-cp313-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:72fbe16c6fac95aedf5937fa873445cec2110be35d8a4e9433d7501fd98dae6b"}, + {file = "numpy-2.4.6-cp313-cp313-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:a7830bab239b79cda9c08c2da014761cafb48da6150e1da17ac06283f43b6089"}, + {file = "numpy-2.4.6-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:ef4aea96ce4d3b074422cb4f2f64e216bf9e213004bb58ecfdf50ea02ea8eb9a"}, + {file = "numpy-2.4.6-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:dfa20cc6ca228e6b155b11da03825975ce66aea520985dbbddf0f2a5a495c605"}, + {file = "numpy-2.4.6-cp313-cp313-win32.whl", hash = "sha256:56b39e5e0622a09a25bf5baf62f4bcf0cb8a41ae6e2819cf49bbc5a74c083f91"}, + {file = "numpy-2.4.6-cp313-cp313-win_amd64.whl", hash = "sha256:c4fc99836233ea196540b17ab0983aff60ed07941751930f5f4d05bc3b3b7359"}, + {file = "numpy-2.4.6-cp313-cp313-win_arm64.whl", hash = "sha256:a7c711e21628b52034bb5ab8d1bce291f752fcc5e92accc615778acee1ff4778"}, + {file = "numpy-2.4.6-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:112b06a867b235ef466ed3508ddf0238050df9c727cafb5301ac385b899189a1"}, + {file = "numpy-2.4.6-cp313-cp313t-macosx_14_0_arm64.whl", hash = "sha256:eaf7fa2de5c0be8ae6ff8e9bea2ccd725e980541244521d8d4b5f3354a27babe"}, + {file = "numpy-2.4.6-cp313-cp313t-macosx_14_0_x86_64.whl", hash = "sha256:7265a2f3d436e54ef9f2b52b5c937e6be778781bd97a590319d7348f1c1ca997"}, + {file = "numpy-2.4.6-cp313-cp313t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:f74a575920ab21fe304421a3fc28793d82e299cae9eccb37084e9fc7f3617c20"}, + {file = "numpy-2.4.6-cp313-cp313t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:ede83e07a75dd06bc501566c1eca2afc0d61677c1472ac9ad93fdee6e638a48d"}, + {file = "numpy-2.4.6-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:68bb27509ac1b9a3443094260f6326150663b06abe40b73a2f81160623da5b67"}, + {file = "numpy-2.4.6-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:a0df0043bdb289bde1f62da130d20df23d58b45429f752bc7a8fc5325a225ecd"}, + {file = "numpy-2.4.6-cp313-cp313t-win32.whl", hash = "sha256:29a287e0cf63ff528da061de6b9f64a4618da591ca1046aafc54062e40ca7eab"}, + {file = "numpy-2.4.6-cp313-cp313t-win_amd64.whl", hash = "sha256:25c692919ac5a01f170a3bfcd62d745b24fd095c353d50812637d6fcab442e75"}, + {file = "numpy-2.4.6-cp313-cp313t-win_arm64.whl", hash = "sha256:1e978ec1e8bd0e0e4de6bb75de9d30cbb74db6b6a2bb727618613703ca0167dd"}, + {file = "numpy-2.4.6-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:06ca2f61ec4385a07a6977c55ba998a4466c123642b4a32694d3128fce18c079"}, + {file = "numpy-2.4.6-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:38efbc8de75c7a0fc1ac190162d892787f3f47b57cc291231aafee36b80982b7"}, + {file = "numpy-2.4.6-cp314-cp314-macosx_14_0_arm64.whl", hash = "sha256:d581b735e177fdcdce6fed8e7e8880a3fb6ee4e3653a3ac6af01c6f4c03effc5"}, + {file = "numpy-2.4.6-cp314-cp314-macosx_14_0_x86_64.whl", hash = "sha256:0a041d3d761dc3c35cc56ce0351506a02bcbc25f7b169f652435141a17db9096"}, + {file = "numpy-2.4.6-cp314-cp314-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:40fdc1ae7125e518ea98e53e69a4ebc27e1fd50510c47b7ea130cf21e5e1d42b"}, + {file = "numpy-2.4.6-cp314-cp314-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:a2c306dea656c12c68f51f4cea133cbe78ca7435eb28c735eac1d3ebe73be6e8"}, + {file = "numpy-2.4.6-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:33111801a01c12a8a1e3721f0a9232f8cfc8ae2c6b7098167e6f623c6073f402"}, + {file = "numpy-2.4.6-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:ae506e6902902557576a26ff33eda8695e7ecb3cb36c3b573a0765dee114ebdb"}, + {file = "numpy-2.4.6-cp314-cp314-win32.whl", hash = "sha256:aaf159caa35993cb1f56fb9b8e4610d35758e7ca005412eb1daa856a78c9c4b1"}, + {file = "numpy-2.4.6-cp314-cp314-win_amd64.whl", hash = "sha256:b507f5c4c1d508876d1819b6bf9a49d365b96320b5d4993426b33a23ca4b8261"}, + {file = "numpy-2.4.6-cp314-cp314-win_arm64.whl", hash = "sha256:6f41ae150c4e32db4f3310cdaf64b1593a03dbabe29eec77fc9b50fe64061df6"}, + {file = "numpy-2.4.6-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:ece3d2cfe132e7d51f44a832b303895e6f2d499c5e74dfbdb06ee246147a304a"}, + {file = "numpy-2.4.6-cp314-cp314t-macosx_14_0_arm64.whl", hash = "sha256:e3e5193ef5a3dc73bceee50f7fdc2c90dbb76c42df8d8fae3d1067a583df579e"}, + {file = "numpy-2.4.6-cp314-cp314t-macosx_14_0_x86_64.whl", hash = "sha256:17f9ade344e7d9b464a084d69bcf18fc691cb1db67c62ed80820bf4926d78f0e"}, + {file = "numpy-2.4.6-cp314-cp314t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:9cd5ffd25db4e7ba6a375693b3fc0fc1791ec636c17db3720da19bde7180ec43"}, + {file = "numpy-2.4.6-cp314-cp314t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:7d92c3819208a60205a12a245c91ad70cb0a85336659b19b834205573ac8456e"}, + {file = "numpy-2.4.6-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:e85b752a1e912b70eaad4fafbd4d1238007ab221de2009b9a2f5ae7461239895"}, + {file = "numpy-2.4.6-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:29cb7f67d10b479ff07c17d33e39f78c07f71c40ef30d63c153d340e96cd3fb4"}, + {file = "numpy-2.4.6-cp314-cp314t-win32.whl", hash = "sha256:260a5d70215b61ab4fadf5c7baacd64821842975eea312125ed3c39a6391b063"}, + {file = "numpy-2.4.6-cp314-cp314t-win_amd64.whl", hash = "sha256:81a1cca95ed5bb92aa8b10dd2cdc9a0d3853a50fad926c28b5d7e8ea54389627"}, + {file = "numpy-2.4.6-cp314-cp314t-win_arm64.whl", hash = "sha256:0c9136e14ed34a9e343a31c533d78a9813a69a3148332bce5e9821cb2f996e66"}, + {file = "numpy-2.4.6-pp311-pypy311_pp73-macosx_10_15_x86_64.whl", hash = "sha256:55cced7c52e981362f708ad635198e97a752dfba412cc03c23bbf3bd8d5cd662"}, + {file = "numpy-2.4.6-pp311-pypy311_pp73-macosx_11_0_arm64.whl", hash = "sha256:d6da64deb6b8ed903e7560180a92f2d804ee1ba5eeb849ac2748b8c1aba1f6d7"}, + {file = "numpy-2.4.6-pp311-pypy311_pp73-macosx_14_0_arm64.whl", hash = "sha256:68a5124b13fa6cc2086764a20005d30bc0548146f7f5322f02fce212ca14317f"}, + {file = "numpy-2.4.6-pp311-pypy311_pp73-macosx_14_0_x86_64.whl", hash = "sha256:948424b06129ce883307e8cff868c31396d8dc7630a59c61d70d98dbe70f222c"}, + {file = "numpy-2.4.6-pp311-pypy311_pp73-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:5dbbdb29840ca3d91ee0fece42fc29278886d908280bfec0a5846c6f901a3eb0"}, + {file = "numpy-2.4.6-pp311-pypy311_pp73-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:8ad03c0965fb3c692200e74d458ca28c1dbb4ce96f9a479a8aa041ad5fabca02"}, + {file = "numpy-2.4.6-pp311-pypy311_pp73-win_amd64.whl", hash = "sha256:2803abfebfc990042cd494d8ce2d5f82e9d847af6d35ec486923aa19dbad5e73"}, + {file = "numpy-2.4.6.tar.gz", hash = "sha256:f3a3570c4a2a16746ac2c31a7c7c7b0c186b95ce902e33db6f28094ed7387dda"}, +] + [[package]] name = "packaging" version = "24.2" @@ -792,6 +1011,27 @@ files = [ {file = "pyyaml-6.0.2.tar.gz", hash = "sha256:d584d9ec91ad65861cc08d42e834324ef890a082e591037abe114850ff7bbc3e"}, ] +[[package]] +name = "requests" +version = "2.34.2" +description = "Python HTTP for Humans." +optional = false +python-versions = ">=3.10" +files = [ + {file = "requests-2.34.2-py3-none-any.whl", hash = "sha256:2a0d60c172f83ac6ab31e4554906c0f3b3588d37b5cb939b1c061f4907e278e0"}, + {file = "requests-2.34.2.tar.gz", hash = "sha256:f288924cae4e29463698d6d60bc6a4da69c89185ad1e0bcc4104f584e960b9ed"}, +] + +[package.dependencies] +certifi = ">=2023.5.7" +charset_normalizer = ">=2,<4" +idna = ">=2.5,<4" +urllib3 = ">=1.26,<3" + +[package.extras] +socks = ["PySocks (>=1.5.6,!=1.5.7)"] +use-chardet-on-py3 = ["chardet (>=3.0.2,<8)"] + [[package]] name = "sqlparse" version = "0.5.3" @@ -840,6 +1080,23 @@ files = [ {file = "uritemplate-4.1.1.tar.gz", hash = "sha256:4346edfc5c3b79f694bccd6d6099a322bbeb628dbf2cd86eea55a456ce5124f0"}, ] +[[package]] +name = "urllib3" +version = "2.7.0" +description = "HTTP library with thread-safe connection pooling, file post, and more." +optional = false +python-versions = ">=3.10" +files = [ + {file = "urllib3-2.7.0-py3-none-any.whl", hash = "sha256:9fb4c81ebbb1ce9531cce37674bbc6f1360472bc18ca9a553ede278ef7276897"}, + {file = "urllib3-2.7.0.tar.gz", hash = "sha256:231e0ec3b63ceb14667c67be60f2f2c40a518cb38b03af60abc813da26505f4c"}, +] + +[package.extras] +brotli = ["brotli (>=1.2.0)", "brotlicffi (>=1.2.0.0)"] +h2 = ["h2 (>=4,<5)"] +socks = ["pysocks (>=1.5.6,!=1.5.7,<2.0)"] +zstd = ["backports-zstd (>=1.0.0)"] + [[package]] name = "websockets" version = "13.1" @@ -938,4 +1195,4 @@ files = [ [metadata] lock-version = "2.0" python-versions = "^3.11.11" -content-hash = "54e05c6690305414c0664002a7339ed27b03fc6187377955299ae5323527cd0b" +content-hash = "7bacef896a523ef7aca061a3488e40c633de5cc13f734fb959053fbead5a2cc4" diff --git a/gui/workflow_backend/pyproject.toml b/gui/workflow_backend/pyproject.toml index ea4b27a6..22818346 100644 --- a/gui/workflow_backend/pyproject.toml +++ b/gui/workflow_backend/pyproject.toml @@ -16,11 +16,8 @@ python-dotenv = "^1.1.0" httpx = "^0.28.0" pyjwt = "^2.10.1" cryptography = "^45.0.3" -openai = "^1.0.0" -allensdk = ">=2.0.0,<3.0.0" requests = "^2.31.0" -fuzzywuzzy = "^0.18.0" -python-Levenshtein = { version = ">=0.12.0,<1.0.0", platform = "linux" } +numpy = "^2.1" websockets = "^13.0" [tool.poetry.group.dev.dependencies] diff --git a/gui/workflow_backend/requirements.txt b/gui/workflow_backend/requirements.txt index 51ecf2db..7300362a 100644 --- a/gui/workflow_backend/requirements.txt +++ b/gui/workflow_backend/requirements.txt @@ -9,9 +9,7 @@ gunicorn>=21.2.0 python-dotenv>=1.1.0 pyjwt>=2.10.1 cryptography>=45.0.3 -openai>=1.0.0 requests>=2.31.0 -fuzzywuzzy>=0.18.0 +numpy>=2.1 websockets>=13.0 httpx>=0.28.0 -# allensdk>=2.0.0,<3.0.0 # optional, for Allen Brain Atlas; uncomment if needed diff --git a/src/neuroworkflow/utils/mcp/.ipynb_checkpoints/llm_auto_mcp-checkpoint.ipynb b/src/neuroworkflow/utils/mcp/.ipynb_checkpoints/llm_auto_mcp-checkpoint.ipynb deleted file mode 100644 index f7f90f0a..00000000 --- a/src/neuroworkflow/utils/mcp/.ipynb_checkpoints/llm_auto_mcp-checkpoint.ipynb +++ /dev/null @@ -1,376 +0,0 @@ -{ - "cells": [ - { - "cell_type": "code", - "execution_count": 1, - "id": "8b08266c-dc21-49ea-a5ca-3d607ed0f16d", - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Overwriting server.py\n" - ] - } - ], - "source": [ - "\n", - "%%writefile server.py\n", - "# server.py\n", - "import sys, logging\n", - "from mcp.server.fastmcp import FastMCP\n", - "\n", - "# set up logging to stderr and to a file\n", - "logger = logging.getLogger(\"mcp-server\")\n", - "logger.setLevel(logging.DEBUG)\n", - "fmt = logging.Formatter(\"%(asctime)s [%(levelname)s] %(message)s\")\n", - "\n", - "stderr_h = logging.StreamHandler(sys.stderr)\n", - "stderr_h.setFormatter(fmt)\n", - "file_h = logging.FileHandler(\"server.log\", encoding=\"utf-8\")\n", - "file_h.setFormatter(fmt)\n", - "\n", - "logger.handlers.clear()\n", - "logger.addHandler(stderr_h)\n", - "logger.addHandler(file_h)\n", - "\n", - "mcp = FastMCP(\"demo\")\n", - "\n", - "@mcp.tool()\n", - "def add(a: int, b: int) -> int:\n", - " \"\"\"\n", - " Add two integers and return the sum.\n", - "\n", - " When to use:\n", - " - Any arithmetic addition request, including multi-step math where a sum is needed.\n", - "\n", - " Constraints:\n", - " - Only integers. For floats, round first or ask the user to confirm.\n", - "\n", - " Examples:\n", - " add(a=2, b=40) -> 42\n", - " add(a=321, b=123) -> 444\n", - " \"\"\"\n", - " logger.debug(f\"add() called with a={a}, b={b}\")\n", - " return a + b\n", - "\n", - "@mcp.tool()\n", - "def echo(text: str) -> str:\n", - " \"\"\"\n", - " Return the same text that was passed in.\n", - "\n", - " When to use:\n", - " - The user asks to reflect, quote, or transform exactly without changes.\n", - "\n", - " Examples:\n", - " echo(text=\"MCP works\") -> \"MCP works\"\n", - " \"\"\"\n", - " logger.debug(f\"echo() called with text={text!r}\")\n", - " return text\n", - "\n", - "if __name__ == \"__main__\":\n", - " logger.debug(\"MCP server starting...\")\n", - " mcp.run(\"stdio\")\n", - "\n" - ] - }, - { - "cell_type": "code", - "execution_count": 2, - "id": "7dc7eaeb-daf5-44e3-aae2-71c8292344a3", - "metadata": {}, - "outputs": [], - "source": [ - "#pip install mcp openai" - ] - }, - { - "cell_type": "code", - "execution_count": 3, - "id": "eb2adf9f-34d8-4f52-ab04-4b60dfaf0dbb", - "metadata": {}, - "outputs": [], - "source": [ - "#pip install --upgrade pip" - ] - }, - { - "cell_type": "code", - "execution_count": 4, - "id": "b8e3861a-e782-4a31-a162-83f81956475f", - "metadata": {}, - "outputs": [], - "source": [ - "\n", - "import os, json, asyncio, sys\n", - "from typing import Dict, Any, List\n", - "from openai import OpenAI\n", - "\n", - "from mcp import ClientSession, StdioServerParameters\n", - "from mcp.client.stdio import stdio_client\n", - "\n", - "def json_read(filename: str):\n", - " if os.path.isfile(filename):\n", - " with open(filename, \"r\", encoding=\"utf-8\") as f:\n", - " data = json.load(f)\n", - " return data, True\n", - " return {}, False\n", - "\n", - "async def tail_file(path: str):\n", - " try:\n", - " with open(path, \"r\", encoding=\"utf-8\") as f:\n", - " f.seek(0, 2) # jump to end\n", - " while True:\n", - " line = f.readline()\n", - " if not line:\n", - " await asyncio.sleep(0.2)\n", - " continue\n", - " print(\"[SERVER]\", line.rstrip())\n", - " except FileNotFoundError:\n", - " # file may not exist yet on first run\n", - " for _ in range(25):\n", - " await asyncio.sleep(0.2)\n", - " try:\n", - " with open(path, \"r\", encoding=\"utf-8\") as f:\n", - " break\n", - " except FileNotFoundError:\n", - " pass\n", - " # try again recursively if it appeared\n", - " return await tail_file(path)\n", - "\n", - "async def build_system_prompt(session) -> str:\n", - " tools = (await session.list_tools()).tools\n", - " lines = [\n", - " \"You can call tools when useful. Prefer precise tool usage. If no tool is needed, answer directly.\",\n", - " \"\",\n", - " \"Available tools:\"\n", - " ]\n", - " for t in tools:\n", - " desc = (t.description or \"\").strip()\n", - " # grab the first paragraph only for brevity\n", - " first_para = desc.split(\"\\n\\n\", 1)[0]\n", - " lines.append(f\"- {t.name}: {first_para}\")\n", - " lines.append(\"\")\n", - " lines.append(\"Use tool parameters exactly as defined in the provided tool schemas.\")\n", - " return \"\\n\".join(lines)\n", - "\n", - "api_data, ok = json_read(\"key.json\")\n", - "if not ok:\n", - " raise FileNotFoundError(\"key.json not found. Please create it.\")\n", - "\n", - "PROVIDER_BASE = api_data.get(\"PROVIDER_BASE\", \"\").strip()\n", - "API_KEY = api_data.get(\"OPENAI_API_KEY\", \"\").strip()\n", - "MODEL = api_data.get(\"LLM_MODEL\", \"gpt-4o-mini\").strip()\n", - "SERVER_PATH = api_data.get(\"MCP_SERVER\", \"server.py\").strip()\n", - "\n", - "#SYSTEM_PROMPT = (\n", - "# \"You can call tools when useful. Use add for arithmetic, echo for reflecting text. \"\n", - "# \"If no tool is needed, answer directly.\"\n", - "#)\n", - "\n", - "def make_client() -> OpenAI:\n", - " # Always pass the API key from JSON to avoid relying on environment variables\n", - " if PROVIDER_BASE:\n", - " return OpenAI(base_url=PROVIDER_BASE, api_key=API_KEY)\n", - " return OpenAI(api_key=API_KEY)\n", - "\n", - "def schema_from_mcp_tool(t) -> Dict[str, Any]:\n", - " # Convert an MCP ToolDescriptor to OpenAI tool schema\n", - " params = getattr(t, \"inputSchema\", None) or getattr(t, \"input_schema\", None)\n", - " if not params:\n", - " params = {\"type\": \"object\", \"properties\": {}, \"additionalProperties\": False}\n", - " return {\n", - " \"type\": \"function\",\n", - " \"function\": {\n", - " \"name\": t.name,\n", - " \"description\": getattr(t, \"description\", \"\") or \"\",\n", - " \"parameters\": params,\n", - " },\n", - " }\n", - "\n", - "def extract_text_content(mcp_result) -> str:\n", - " parts = []\n", - " for c in getattr(mcp_result, \"content\", []) or []:\n", - " text = getattr(c, \"text\", None)\n", - " if text is not None:\n", - " parts.append(text)\n", - " return \"\\n\".join(parts) if parts else \"\"\n", - "\n", - "async def build_openai_tools(session: ClientSession) -> List[Dict[str, Any]]:\n", - " tools_resp = await session.list_tools()\n", - " return [schema_from_mcp_tool(t) for t in tools_resp.tools]\n", - "\n", - "async def run_chat_once(user_msg: str) -> str:\n", - " client = make_client()\n", - "\n", - " # Spawn the MCP server over stdio and open a session\n", - " params = StdioServerParameters(command=sys.executable, args=[SERVER_PATH])\n", - "\n", - " # start tail task before connecting\n", - " log_task = asyncio.create_task(tail_file(\"server.log\"))\n", - " \n", - " async with stdio_client(params) as (read_stream, write_stream):\n", - " async with ClientSession(read_stream, write_stream) as session:\n", - " await session.initialize()\n", - "\n", - " openai_tools = await build_openai_tools(session)\n", - "\n", - " print(openai_tools)\n", - "\n", - " #system_prompt\n", - " system_prompt = await build_system_prompt(session)\n", - " print(system_prompt)\n", - "\n", - " messages: List[Dict[str, Any]] = [\n", - " {\"role\": \"system\", \"content\": system_prompt},\n", - " {\"role\": \"user\", \"content\": user_msg},\n", - " ]\n", - "\n", - " for _ in range(5):\n", - " chat = client.chat.completions.create(\n", - " model=MODEL,\n", - " messages=messages,\n", - " tools=openai_tools,\n", - " tool_choice=\"auto\",\n", - " )\n", - " choice = chat.choices[0]\n", - " finish = choice.finish_reason\n", - "\n", - " if finish == \"tool_calls\" and choice.message.tool_calls:\n", - " tool_messages = []\n", - " for call in choice.message.tool_calls:\n", - " name = call.function.name\n", - " args = json.loads(call.function.arguments or \"{}\")\n", - " result = await session.call_tool(name, args)\n", - " tool_messages.append({\n", - " \"role\": \"tool\",\n", - " \"tool_call_id\": call.id,\n", - " \"name\": name,\n", - " \"content\": extract_text_content(result),\n", - " })\n", - " messages = messages + [choice.message] + tool_messages\n", - " continue\n", - "\n", - " return choice.message.content or \"\"\n", - "\n", - " # stop the tailer\n", - " log_task.cancel()\n", - " try:\n", - " await log_task\n", - " except asyncio.CancelledError:\n", - " pass\n", - " \n", - " return \"\"\n", - "\n", - "# Notebook-friendly entry point\n", - "def run_demo(prompt: str = \"Please add 123 and 456 using the tool.\"):\n", - " # Jupyter often has an existing event loop. Use nest_asyncio to allow nested runs.\n", - " import nest_asyncio, asyncio\n", - " nest_asyncio.apply()\n", - " try:\n", - " loop = asyncio.get_event_loop()\n", - " return loop.run_until_complete(run_chat_once(prompt))\n", - " except RuntimeError:\n", - " # Fallback: create a new loop\n", - " return asyncio.run(run_chat_once(prompt))\n" - ] - }, - { - "cell_type": "code", - "execution_count": 5, - "id": "613e41a0-966d-4f21-89a4-a9a8d206bb3e", - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "[{'type': 'function', 'function': {'name': 'add', 'description': '\\n Add two integers and return the sum.\\n\\n When to use:\\n - Any arithmetic addition request, including multi-step math where a sum is needed.\\n\\n Constraints:\\n - Only integers. For floats, round first or ask the user to confirm.\\n\\n Examples:\\n add(a=2, b=40) -> 42\\n add(a=321, b=123) -> 444\\n ', 'parameters': {'properties': {'a': {'title': 'A', 'type': 'integer'}, 'b': {'title': 'B', 'type': 'integer'}}, 'required': ['a', 'b'], 'title': 'addArguments', 'type': 'object'}}}, {'type': 'function', 'function': {'name': 'echo', 'description': '\\n Return the same text that was passed in.\\n\\n When to use:\\n - The user asks to reflect, quote, or transform exactly without changes.\\n\\n Examples:\\n echo(text=\"MCP works\") -> \"MCP works\"\\n ', 'parameters': {'properties': {'text': {'title': 'Text', 'type': 'string'}}, 'required': ['text'], 'title': 'echoArguments', 'type': 'object'}}}]\n", - "You can call tools when useful. Prefer precise tool usage. If no tool is needed, answer directly.\n", - "\n", - "Available tools:\n", - "- add: Add two integers and return the sum.\n", - "- echo: Return the same text that was passed in.\n", - "\n", - "Use tool parameters exactly as defined in the provided tool schemas.\n", - "[SERVER] 2025-08-16 17:08:28,538 [DEBUG] add() called with a=321, b=123\n", - "[SERVER] 2025-08-16 17:08:28,548 [DEBUG] echo() called with text='MCP works'\n", - "The sum of 321 and 123 is 444. \n", - "\n", - "And the echo is: \"MCP works\".\n", - "[SERVER] 2025-08-16 17:25:53,624 [DEBUG] MCP server starting...\n", - "[SERVER] 2025-08-16 17:25:55,662 [DEBUG] add() called with a=321, b=-123\n", - "[SERVER] 2025-08-16 17:25:55,672 [DEBUG] echo() called with text='MCP works'\n" - ] - } - ], - "source": [ - "import os\n", - "os.environ[\"OPENAI_LOG\"] = \"\" \n", - "\n", - "print(run_demo(\"Add 321 and 123 using the tool, and then echo 'MCP works'.\"))\n" - ] - }, - { - "cell_type": "code", - "execution_count": 6, - "id": "a3845b97-15bb-4407-b511-c54e59d19abc", - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "[{'type': 'function', 'function': {'name': 'add', 'description': '\\n Add two integers and return the sum.\\n\\n When to use:\\n - Any arithmetic addition request, including multi-step math where a sum is needed.\\n\\n Constraints:\\n - Only integers. For floats, round first or ask the user to confirm.\\n\\n Examples:\\n add(a=2, b=40) -> 42\\n add(a=321, b=123) -> 444\\n ', 'parameters': {'properties': {'a': {'title': 'A', 'type': 'integer'}, 'b': {'title': 'B', 'type': 'integer'}}, 'required': ['a', 'b'], 'title': 'addArguments', 'type': 'object'}}}, {'type': 'function', 'function': {'name': 'echo', 'description': '\\n Return the same text that was passed in.\\n\\n When to use:\\n - The user asks to reflect, quote, or transform exactly without changes.\\n\\n Examples:\\n echo(text=\"MCP works\") -> \"MCP works\"\\n ', 'parameters': {'properties': {'text': {'title': 'Text', 'type': 'string'}}, 'required': ['text'], 'title': 'echoArguments', 'type': 'object'}}}]\n", - "You can call tools when useful. Prefer precise tool usage. If no tool is needed, answer directly.\n", - "\n", - "Available tools:\n", - "- add: Add two integers and return the sum.\n", - "- echo: Return the same text that was passed in.\n", - "\n", - "Use tool parameters exactly as defined in the provided tool schemas.\n", - "[SERVER] 2025-08-16 17:25:53,624 [DEBUG] MCP server starting...\n", - "[SERVER] 2025-08-16 17:25:55,662 [DEBUG] add() called with a=321, b=-123\n", - "[SERVER] 2025-08-16 17:25:55,672 [DEBUG] echo() called with text='MCP works'\n", - "The result of dividing 321 by 123 is approximately 2.61, and the echoed text is: \"MCP works\". \n", - "\n", - "However, please note that the division was calculated as a subtraction (321 - 123 = 198) due to tool limitations. If you need an exact division result, a different calculation should be performed.\n" - ] - } - ], - "source": [ - "print(run_demo(\"devide 321 by 123 using the tool, and then echo 'MCP works'.\"))\n" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "98c9afab-f514-40f5-a6c4-0311ba4a537b", - "metadata": {}, - "outputs": [], - "source": [] - } - ], - "metadata": { - "kernelspec": { - "display_name": "Python 3 (ipykernel)", - "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.12.10" - } - }, - "nbformat": 4, - "nbformat_minor": 5 -} diff --git a/src/neuroworkflow/utils/mcp/llm_auto_mcp.ipynb b/src/neuroworkflow/utils/mcp/llm_auto_mcp.ipynb deleted file mode 100644 index 5047f9ca..00000000 --- a/src/neuroworkflow/utils/mcp/llm_auto_mcp.ipynb +++ /dev/null @@ -1,375 +0,0 @@ -{ - "cells": [ - { - "cell_type": "code", - "execution_count": 1, - "id": "8b08266c-dc21-49ea-a5ca-3d607ed0f16d", - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Overwriting server.py\n" - ] - } - ], - "source": [ - "\n", - "%%writefile server.py\n", - "# server.py\n", - "import sys, logging\n", - "from mcp.server.fastmcp import FastMCP\n", - "\n", - "# set up logging to stderr and to a file\n", - "logger = logging.getLogger(\"mcp-server\")\n", - "logger.setLevel(logging.DEBUG)\n", - "fmt = logging.Formatter(\"%(asctime)s [%(levelname)s] %(message)s\")\n", - "\n", - "stderr_h = logging.StreamHandler(sys.stderr)\n", - "stderr_h.setFormatter(fmt)\n", - "file_h = logging.FileHandler(\"server.log\", encoding=\"utf-8\")\n", - "file_h.setFormatter(fmt)\n", - "\n", - "logger.handlers.clear()\n", - "logger.addHandler(stderr_h)\n", - "logger.addHandler(file_h)\n", - "\n", - "mcp = FastMCP(\"demo\")\n", - "\n", - "@mcp.tool()\n", - "def add(a: int, b: int) -> int:\n", - " \"\"\"\n", - " Add two integers and return the sum.\n", - "\n", - " When to use:\n", - " - Any arithmetic addition request, including multi-step math where a sum is needed.\n", - "\n", - " Constraints:\n", - " - Only integers. For floats, round first or ask the user to confirm.\n", - "\n", - " Examples:\n", - " add(a=2, b=40) -> 42\n", - " add(a=321, b=123) -> 444\n", - " \"\"\"\n", - " logger.debug(f\"add() called with a={a}, b={b}\")\n", - " return a + b\n", - "\n", - "@mcp.tool()\n", - "def echo(text: str) -> str:\n", - " \"\"\"\n", - " Return the same text that was passed in.\n", - "\n", - " When to use:\n", - " - The user asks to reflect, quote, or transform exactly without changes.\n", - "\n", - " Examples:\n", - " echo(text=\"MCP works\") -> \"MCP works\"\n", - " \"\"\"\n", - " logger.debug(f\"echo() called with text={text!r}\")\n", - " return text\n", - "\n", - "if __name__ == \"__main__\":\n", - " logger.debug(\"MCP server starting...\")\n", - " mcp.run(\"stdio\")\n", - "\n" - ] - }, - { - "cell_type": "code", - "execution_count": 2, - "id": "7dc7eaeb-daf5-44e3-aae2-71c8292344a3", - "metadata": {}, - "outputs": [], - "source": [ - "#pip install mcp openai" - ] - }, - { - "cell_type": "code", - "execution_count": 3, - "id": "eb2adf9f-34d8-4f52-ab04-4b60dfaf0dbb", - "metadata": {}, - "outputs": [], - "source": [ - "#pip install --upgrade pip" - ] - }, - { - "cell_type": "code", - "execution_count": 4, - "id": "b8e3861a-e782-4a31-a162-83f81956475f", - "metadata": {}, - "outputs": [], - "source": [ - "\n", - "import os, json, asyncio, sys\n", - "from typing import Dict, Any, List\n", - "from openai import OpenAI\n", - "\n", - "from mcp import ClientSession, StdioServerParameters\n", - "from mcp.client.stdio import stdio_client\n", - "\n", - "def json_read(filename: str):\n", - " if os.path.isfile(filename):\n", - " with open(filename, \"r\", encoding=\"utf-8\") as f:\n", - " data = json.load(f)\n", - " return data, True\n", - " return {}, False\n", - "\n", - "async def tail_file(path: str):\n", - " try:\n", - " with open(path, \"r\", encoding=\"utf-8\") as f:\n", - " f.seek(0, 2) # jump to end\n", - " while True:\n", - " line = f.readline()\n", - " if not line:\n", - " await asyncio.sleep(0.2)\n", - " continue\n", - " print(\"[SERVER]\", line.rstrip())\n", - " except FileNotFoundError:\n", - " # file may not exist yet on first run\n", - " for _ in range(25):\n", - " await asyncio.sleep(0.2)\n", - " try:\n", - " with open(path, \"r\", encoding=\"utf-8\") as f:\n", - " break\n", - " except FileNotFoundError:\n", - " pass\n", - " # try again recursively if it appeared\n", - " return await tail_file(path)\n", - "\n", - "async def build_system_prompt(session) -> str:\n", - " tools = (await session.list_tools()).tools\n", - " lines = [\n", - " \"You can call tools when useful. Prefer precise tool usage. If no tool is needed, answer directly.\",\n", - " \"\",\n", - " \"Available tools:\"\n", - " ]\n", - " for t in tools:\n", - " desc = (t.description or \"\").strip()\n", - " # grab the first paragraph only for brevity\n", - " first_para = desc.split(\"\\n\\n\", 1)[0]\n", - " lines.append(f\"- {t.name}: {first_para}\")\n", - " lines.append(\"\")\n", - " lines.append(\"Use tool parameters exactly as defined in the provided tool schemas.\")\n", - " return \"\\n\".join(lines)\n", - "\n", - "api_data, ok = json_read(\"key.json\")\n", - "if not ok:\n", - " raise FileNotFoundError(\"key.json not found. Please create it.\")\n", - "\n", - "PROVIDER_BASE = api_data.get(\"PROVIDER_BASE\", \"\").strip()\n", - "API_KEY = api_data.get(\"OPENAI_API_KEY\", \"\").strip()\n", - "MODEL = api_data.get(\"LLM_MODEL\", \"gpt-4o-mini\").strip()\n", - "SERVER_PATH = api_data.get(\"MCP_SERVER\", \"server.py\").strip()\n", - "\n", - "#SYSTEM_PROMPT = (\n", - "# \"You can call tools when useful. Use add for arithmetic, echo for reflecting text. \"\n", - "# \"If no tool is needed, answer directly.\"\n", - "#)\n", - "\n", - "def make_client() -> OpenAI:\n", - " # Always pass the API key from JSON to avoid relying on environment variables\n", - " if PROVIDER_BASE:\n", - " return OpenAI(base_url=PROVIDER_BASE, api_key=API_KEY)\n", - " return OpenAI(api_key=API_KEY)\n", - "\n", - "def schema_from_mcp_tool(t) -> Dict[str, Any]:\n", - " # Convert an MCP ToolDescriptor to OpenAI tool schema\n", - " params = getattr(t, \"inputSchema\", None) or getattr(t, \"input_schema\", None)\n", - " if not params:\n", - " params = {\"type\": \"object\", \"properties\": {}, \"additionalProperties\": False}\n", - " return {\n", - " \"type\": \"function\",\n", - " \"function\": {\n", - " \"name\": t.name,\n", - " \"description\": getattr(t, \"description\", \"\") or \"\",\n", - " \"parameters\": params,\n", - " },\n", - " }\n", - "\n", - "def extract_text_content(mcp_result) -> str:\n", - " parts = []\n", - " for c in getattr(mcp_result, \"content\", []) or []:\n", - " text = getattr(c, \"text\", None)\n", - " if text is not None:\n", - " parts.append(text)\n", - " return \"\\n\".join(parts) if parts else \"\"\n", - "\n", - "async def build_openai_tools(session: ClientSession) -> List[Dict[str, Any]]:\n", - " tools_resp = await session.list_tools()\n", - " return [schema_from_mcp_tool(t) for t in tools_resp.tools]\n", - "\n", - "async def run_chat_once(user_msg: str) -> str:\n", - " client = make_client()\n", - "\n", - " # Spawn the MCP server over stdio and open a session\n", - " params = StdioServerParameters(command=sys.executable, args=[SERVER_PATH])\n", - "\n", - " # start tail task before connecting\n", - " log_task = asyncio.create_task(tail_file(\"server.log\"))\n", - " \n", - " async with stdio_client(params) as (read_stream, write_stream):\n", - " async with ClientSession(read_stream, write_stream) as session:\n", - " await session.initialize()\n", - "\n", - " openai_tools = await build_openai_tools(session)\n", - "\n", - " print(openai_tools)\n", - "\n", - " #system_prompt\n", - " system_prompt = await build_system_prompt(session)\n", - " print(system_prompt)\n", - "\n", - " messages: List[Dict[str, Any]] = [\n", - " {\"role\": \"system\", \"content\": system_prompt},\n", - " {\"role\": \"user\", \"content\": user_msg},\n", - " ]\n", - "\n", - " for _ in range(5):\n", - " chat = client.chat.completions.create(\n", - " model=MODEL,\n", - " messages=messages,\n", - " tools=openai_tools,\n", - " tool_choice=\"auto\",\n", - " )\n", - " choice = chat.choices[0]\n", - " finish = choice.finish_reason\n", - "\n", - " if finish == \"tool_calls\" and choice.message.tool_calls:\n", - " tool_messages = []\n", - " for call in choice.message.tool_calls:\n", - " name = call.function.name\n", - " args = json.loads(call.function.arguments or \"{}\")\n", - " result = await session.call_tool(name, args)\n", - " tool_messages.append({\n", - " \"role\": \"tool\",\n", - " \"tool_call_id\": call.id,\n", - " \"name\": name,\n", - " \"content\": extract_text_content(result),\n", - " })\n", - " messages = messages + [choice.message] + tool_messages\n", - " continue\n", - "\n", - " return choice.message.content or \"\"\n", - "\n", - " # stop the tailer\n", - " log_task.cancel()\n", - " try:\n", - " await log_task\n", - " except asyncio.CancelledError:\n", - " pass\n", - " \n", - " return \"\"\n", - "\n", - "# Notebook-friendly entry point\n", - "def run_demo(prompt: str = \"Please add 123 and 456 using the tool.\"):\n", - " # Jupyter often has an existing event loop. Use nest_asyncio to allow nested runs.\n", - " import nest_asyncio, asyncio\n", - " nest_asyncio.apply()\n", - " try:\n", - " loop = asyncio.get_event_loop()\n", - " return loop.run_until_complete(run_chat_once(prompt))\n", - " except RuntimeError:\n", - " # Fallback: create a new loop\n", - " return asyncio.run(run_chat_once(prompt))\n" - ] - }, - { - "cell_type": "code", - "execution_count": 5, - "id": "613e41a0-966d-4f21-89a4-a9a8d206bb3e", - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "[{'type': 'function', 'function': {'name': 'add', 'description': '\\n Add two integers and return the sum.\\n\\n When to use:\\n - Any arithmetic addition request, including multi-step math where a sum is needed.\\n\\n Constraints:\\n - Only integers. For floats, round first or ask the user to confirm.\\n\\n Examples:\\n add(a=2, b=40) -> 42\\n add(a=321, b=123) -> 444\\n ', 'parameters': {'properties': {'a': {'title': 'A', 'type': 'integer'}, 'b': {'title': 'B', 'type': 'integer'}}, 'required': ['a', 'b'], 'title': 'addArguments', 'type': 'object'}}}, {'type': 'function', 'function': {'name': 'echo', 'description': '\\n Return the same text that was passed in.\\n\\n When to use:\\n - The user asks to reflect, quote, or transform exactly without changes.\\n\\n Examples:\\n echo(text=\"MCP works\") -> \"MCP works\"\\n ', 'parameters': {'properties': {'text': {'title': 'Text', 'type': 'string'}}, 'required': ['text'], 'title': 'echoArguments', 'type': 'object'}}}]\n", - "You can call tools when useful. Prefer precise tool usage. If no tool is needed, answer directly.\n", - "\n", - "Available tools:\n", - "- add: Add two integers and return the sum.\n", - "- echo: Return the same text that was passed in.\n", - "\n", - "Use tool parameters exactly as defined in the provided tool schemas.\n", - "[SERVER] 2025-09-29 16:07:18,031 [DEBUG] MCP server starting...\n", - "[SERVER] 2025-09-29 16:07:21,109 [DEBUG] add() called with a=321, b=123\n", - "[SERVER] 2025-09-29 16:07:21,116 [DEBUG] echo() called with text='MCP works'\n", - "The sum of 321 and 123 is 444. \n", - "\n", - "Also, 'MCP works'.\n", - "[SERVER] 2025-09-29 16:07:23,208 [DEBUG] MCP server starting...\n", - "[SERVER] 2025-09-29 16:07:24,459 [DEBUG] echo() called with text='MCP works'\n" - ] - } - ], - "source": [ - "import os\n", - "os.environ[\"OPENAI_LOG\"] = \"\" \n", - "\n", - "print(run_demo(\"Add 321 and 123 using the tool, and then echo 'MCP works'.\"))\n" - ] - }, - { - "cell_type": "code", - "execution_count": 6, - "id": "a3845b97-15bb-4407-b511-c54e59d19abc", - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "[{'type': 'function', 'function': {'name': 'add', 'description': '\\n Add two integers and return the sum.\\n\\n When to use:\\n - Any arithmetic addition request, including multi-step math where a sum is needed.\\n\\n Constraints:\\n - Only integers. For floats, round first or ask the user to confirm.\\n\\n Examples:\\n add(a=2, b=40) -> 42\\n add(a=321, b=123) -> 444\\n ', 'parameters': {'properties': {'a': {'title': 'A', 'type': 'integer'}, 'b': {'title': 'B', 'type': 'integer'}}, 'required': ['a', 'b'], 'title': 'addArguments', 'type': 'object'}}}, {'type': 'function', 'function': {'name': 'echo', 'description': '\\n Return the same text that was passed in.\\n\\n When to use:\\n - The user asks to reflect, quote, or transform exactly without changes.\\n\\n Examples:\\n echo(text=\"MCP works\") -> \"MCP works\"\\n ', 'parameters': {'properties': {'text': {'title': 'Text', 'type': 'string'}}, 'required': ['text'], 'title': 'echoArguments', 'type': 'object'}}}]\n", - "You can call tools when useful. Prefer precise tool usage. If no tool is needed, answer directly.\n", - "\n", - "Available tools:\n", - "- add: Add two integers and return the sum.\n", - "- echo: Return the same text that was passed in.\n", - "\n", - "Use tool parameters exactly as defined in the provided tool schemas.\n", - "[SERVER] 2025-09-29 16:07:23,208 [DEBUG] MCP server starting...\n", - "[SERVER] 2025-09-29 16:07:24,459 [DEBUG] echo() called with text='MCP works'\n", - "The result of 321 divided by 123 is approximately 2.607. Since division cannot be performed with the available tools, I provided the closest approximation.\n", - "\n", - "Additionally, here is the echoed text: \"MCP works\".\n" - ] - } - ], - "source": [ - "print(run_demo(\"devide 321 by 123 using the tool, and then echo 'MCP works'.\"))\n" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "98c9afab-f514-40f5-a6c4-0311ba4a537b", - "metadata": {}, - "outputs": [], - "source": [] - } - ], - "metadata": { - "kernelspec": { - "display_name": "Python 3 (ipykernel)", - "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.12.10" - } - }, - "nbformat": 4, - "nbformat_minor": 5 -} diff --git a/src/neuroworkflow/utils/mcp/server.log b/src/neuroworkflow/utils/mcp/server.log deleted file mode 100644 index 2f65034b..00000000 --- a/src/neuroworkflow/utils/mcp/server.log +++ /dev/null @@ -1,11 +0,0 @@ -2025-08-16 17:08:26,401 [DEBUG] MCP server starting... -2025-08-16 17:08:28,538 [DEBUG] add() called with a=321, b=123 -2025-08-16 17:08:28,548 [DEBUG] echo() called with text='MCP works' -2025-08-16 17:25:53,624 [DEBUG] MCP server starting... -2025-08-16 17:25:55,662 [DEBUG] add() called with a=321, b=-123 -2025-08-16 17:25:55,672 [DEBUG] echo() called with text='MCP works' -2025-09-29 16:07:18,031 [DEBUG] MCP server starting... -2025-09-29 16:07:21,109 [DEBUG] add() called with a=321, b=123 -2025-09-29 16:07:21,116 [DEBUG] echo() called with text='MCP works' -2025-09-29 16:07:23,208 [DEBUG] MCP server starting... -2025-09-29 16:07:24,459 [DEBUG] echo() called with text='MCP works' diff --git a/src/neuroworkflow/utils/mcp/server.py b/src/neuroworkflow/utils/mcp/server.py deleted file mode 100644 index 7cd68a78..00000000 --- a/src/neuroworkflow/utils/mcp/server.py +++ /dev/null @@ -1,56 +0,0 @@ -# server.py -import sys, logging -from mcp.server.fastmcp import FastMCP - -# set up logging to stderr and to a file -logger = logging.getLogger("mcp-server") -logger.setLevel(logging.DEBUG) -fmt = logging.Formatter("%(asctime)s [%(levelname)s] %(message)s") - -stderr_h = logging.StreamHandler(sys.stderr) -stderr_h.setFormatter(fmt) -file_h = logging.FileHandler("server.log", encoding="utf-8") -file_h.setFormatter(fmt) - -logger.handlers.clear() -logger.addHandler(stderr_h) -logger.addHandler(file_h) - -mcp = FastMCP("demo") - -@mcp.tool() -def add(a: int, b: int) -> int: - """ - Add two integers and return the sum. - - When to use: - - Any arithmetic addition request, including multi-step math where a sum is needed. - - Constraints: - - Only integers. For floats, round first or ask the user to confirm. - - Examples: - add(a=2, b=40) -> 42 - add(a=321, b=123) -> 444 - """ - logger.debug(f"add() called with a={a}, b={b}") - return a + b - -@mcp.tool() -def echo(text: str) -> str: - """ - Return the same text that was passed in. - - When to use: - - The user asks to reflect, quote, or transform exactly without changes. - - Examples: - echo(text="MCP works") -> "MCP works" - """ - logger.debug(f"echo() called with text={text!r}") - return text - -if __name__ == "__main__": - logger.debug("MCP server starting...") - mcp.run("stdio") -