diff --git a/typescript-sdk/apps/dojo/package.json b/typescript-sdk/apps/dojo/package.json index fb630d363..105b8e1e4 100644 --- a/typescript-sdk/apps/dojo/package.json +++ b/typescript-sdk/apps/dojo/package.json @@ -20,6 +20,7 @@ "@ag-ui/pydantic-ai": "workspace:*", "@ag-ui/server-starter": "workspace:*", "@ag-ui/server-starter-all-features": "workspace:*", + "@ag-ui/superoptix": "workspace:*", "@ag-ui/vercel-ai-sdk": "workspace:*", "@ai-sdk/openai": "^1.3.22", "@copilotkit/react-core": "1.9.2", diff --git a/typescript-sdk/apps/dojo/scripts/generate-content-json.ts b/typescript-sdk/apps/dojo/scripts/generate-content-json.ts index d06693c70..99fe7fd48 100644 --- a/typescript-sdk/apps/dojo/scripts/generate-content-json.ts +++ b/typescript-sdk/apps/dojo/scripts/generate-content-json.ts @@ -195,6 +195,12 @@ const agentFilesMapper: Record Record { + return agentKeys.reduce((acc, agentId) => ({ + ...acc, + [agentId]: [path.join(__dirname, integrationsFolderPath, `/superoptix/python/ag_ui_superoptix/endpoint.py`)] + }), {}) } } diff --git a/typescript-sdk/apps/dojo/src/agents.ts b/typescript-sdk/apps/dojo/src/agents.ts index 9b2234e3d..7049d8bd9 100644 --- a/typescript-sdk/apps/dojo/src/agents.ts +++ b/typescript-sdk/apps/dojo/src/agents.ts @@ -12,6 +12,7 @@ import { LangGraphAgent, LangGraphHttpAgent } from "@ag-ui/langgraph"; import { AgnoAgent } from "@ag-ui/agno"; import { LlamaIndexAgent } from "@ag-ui/llamaindex"; import { CrewAIAgent } from "@ag-ui/crewai"; +import { SuperOptiXAgent } from "@ag-ui/superoptix"; import getEnvVars from "./env"; import { mastra } from "./mastra"; import { PydanticAIAgent } from "@ag-ui/pydantic-ai"; @@ -220,4 +221,29 @@ export const agentsIntegrations: AgentIntegrationConfig[] = [ }; }, }, + { + id: "superoptix", + agents: async () => { + return { + agentic_chat: new SuperOptiXAgent({ + url: `${envVars.superoptixUrl || "http://localhost:8000"}/agentic_chat`, + }), + human_in_the_loop: new SuperOptiXAgent({ + url: `${envVars.superoptixUrl || "http://localhost:8000"}/human_in_the_loop`, + }), + tool_based_generative_ui: new SuperOptiXAgent({ + url: `${envVars.superoptixUrl || "http://localhost:8000"}/tool_based_generative_ui`, + }), + agentic_generative_ui: new SuperOptiXAgent({ + url: `${envVars.superoptixUrl || "http://localhost:8000"}/agentic_generative_ui`, + }), + shared_state: new SuperOptiXAgent({ + url: `${envVars.superoptixUrl || "http://localhost:8000"}/shared_state`, + }), + predictive_state_updates: new SuperOptiXAgent({ + url: `${envVars.superoptixUrl || "http://localhost:8000"}/predictive_state_updates`, + }), + }; + }, + }, ]; diff --git a/typescript-sdk/apps/dojo/src/env.ts b/typescript-sdk/apps/dojo/src/env.ts index 4ac83b257..7372fbb2b 100644 --- a/typescript-sdk/apps/dojo/src/env.ts +++ b/typescript-sdk/apps/dojo/src/env.ts @@ -8,6 +8,7 @@ type envVars = { llamaIndexUrl: string; crewAiUrl: string; pydanticAIUrl: string; + superoptixUrl: string; } export default function getEnvVars(): envVars { @@ -21,5 +22,6 @@ export default function getEnvVars(): envVars { llamaIndexUrl: process.env.LLAMA_INDEX_URL || 'http://localhost:9000', crewAiUrl: process.env.CREW_AI_URL || 'http://localhost:9002', pydanticAIUrl: process.env.PYDANTIC_AI_URL || 'http://localhost:9000', + superoptixUrl: process.env.SUPEROPTIX_URL || 'http://localhost:8000', } } \ No newline at end of file diff --git a/typescript-sdk/apps/dojo/src/files.json b/typescript-sdk/apps/dojo/src/files.json index d33d0236f..9847e1ac3 100644 --- a/typescript-sdk/apps/dojo/src/files.json +++ b/typescript-sdk/apps/dojo/src/files.json @@ -1016,5 +1016,161 @@ "language": "python", "type": "file" } + ], + "superoptix::agentic_chat": [ + { + "name": "page.tsx", + "content": "\"use client\";\nimport React, { useState } from \"react\";\nimport \"@copilotkit/react-ui/styles.css\";\nimport \"./style.css\";\nimport { CopilotKit, useCoAgent, useCopilotAction, useCopilotChat } from \"@copilotkit/react-core\";\nimport { CopilotChat } from \"@copilotkit/react-ui\";\n\ninterface AgenticChatProps {\n params: Promise<{\n integrationId: string;\n }>;\n}\n\nconst AgenticChat: React.FC = ({ params }) => {\n const { integrationId } = React.use(params);\n\n return (\n \n \n \n );\n};\n\nconst Chat = () => {\n const [background, setBackground] = useState(\"--copilot-kit-background-color\");\n\n useCopilotAction({\n name: \"change_background\",\n description:\n \"Change the background color of the chat. Can be anything that the CSS background attribute accepts. Regular colors, linear of radial gradients etc.\",\n parameters: [\n {\n name: \"background\",\n type: \"string\",\n description: \"The background. Prefer gradients.\",\n },\n ],\n handler: ({ background }) => {\n setBackground(background);\n return {\n status: \"success\",\n message: `Background changed to ${background}`,\n };\n },\n });\n\n return (\n
\n
\n \n
\n
\n );\n};\n\nexport default AgenticChat;\n", + "language": "typescript", + "type": "file" + }, + { + "name": "style.css", + "content": ".copilotKitInput {\n border-bottom-left-radius: 0.75rem;\n border-bottom-right-radius: 0.75rem;\n border-top-left-radius: 0.75rem;\n border-top-right-radius: 0.75rem;\n border: 1px solid var(--copilot-kit-separator-color) !important;\n}\n \n.copilotKitChat {\n background-color: #fff !important;\n}\n ", + "language": "css", + "type": "file" + }, + { + "name": "README.mdx", + "content": "# 🤖 Agentic Chat with Frontend Tools\n\n## What This Demo Shows\n\nThis demo showcases CopilotKit's **agentic chat** capabilities with **frontend\ntool integration**:\n\n1. **Natural Conversation**: Chat with your Copilot in a familiar chat interface\n2. **Frontend Tool Execution**: The Copilot can directly interacts with your UI\n by calling frontend functions\n3. **Seamless Integration**: Tools defined in the frontend and automatically\n discovered and made available to the agent\n\n## How to Interact\n\nTry asking your Copilot to:\n\n- \"Can you change the background color to something more vibrant?\"\n- \"Make the background a blue to purple gradient\"\n- \"Set the background to a sunset-themed gradient\"\n- \"Change it back to a simple light color\"\n\nYou can also chat about other topics - the agent will respond conversationally\nwhile having the ability to use your UI tools when appropriate.\n\n## ✨ Frontend Tool Integration in Action\n\n**What's happening technically:**\n\n- The React component defines a frontend function using `useCopilotAction`\n- CopilotKit automatically exposes this function to the agent\n- When you make a request, the agent determines whether to use the tool\n- The agent calls the function with the appropriate parameters\n- The UI immediately updates in response\n\n**What you'll see in this demo:**\n\n- The Copilot understands requests to change the background\n- It generates CSS values for colors and gradients\n- When it calls the tool, the background changes instantly\n- The agent provides a conversational response about the changes it made\n\nThis technique of exposing frontend functions to your Copilot can be extended to\nany UI manipulation you want to enable, from theme changes to data filtering,\nnavigation, or complex UI state management!\n", + "language": "markdown", + "type": "file" + }, + { + "name": "endpoint.py", + "content": "\"\"\"\nAG-UI FastAPI server for SuperOptiX.\n\"\"\"\nimport asyncio\nimport uuid\nfrom typing import List, Optional, Dict, Any\nfrom pathlib import Path\n\nfrom fastapi import FastAPI, Request\nfrom fastapi.responses import StreamingResponse\n\nfrom ag_ui.core import (\n RunAgentInput,\n EventType,\n RunStartedEvent,\n RunFinishedEvent,\n RunErrorEvent,\n Message,\n Tool\n)\nfrom ag_ui.core.events import (\n TextMessageChunkEvent,\n ToolCallChunkEvent,\n StepStartedEvent,\n StepFinishedEvent,\n MessagesSnapshotEvent,\n StateSnapshotEvent,\n CustomEvent,\n)\nfrom ag_ui.encoder import EventEncoder\n\nfrom superoptix.runners.dspy_runner import DSPyRunner\n\nQUEUES = {}\nQUEUES_LOCK = asyncio.Lock()\n\n\nasync def create_queue(runner: DSPyRunner) -> asyncio.Queue:\n \"\"\"Create a queue for a runner.\"\"\"\n queue_id = id(runner)\n async with QUEUES_LOCK:\n queue = asyncio.Queue()\n QUEUES[queue_id] = queue\n return queue\n\n\ndef get_queue(runner: DSPyRunner) -> Optional[asyncio.Queue]:\n \"\"\"Get the queue for a runner.\"\"\"\n queue_id = id(runner)\n return QUEUES.get(queue_id)\n\n\nasync def delete_queue(runner: DSPyRunner) -> None:\n \"\"\"Delete the queue for a runner.\"\"\"\n queue_id = id(runner)\n async with QUEUES_LOCK:\n if queue_id in QUEUES:\n del QUEUES[queue_id]\n\n\ndef superoptix_prepare_inputs(\n *,\n state: dict,\n messages: List[Message],\n tools: List[Tool],\n) -> Dict[str, Any]:\n \"\"\"Prepare inputs for SuperOptiX agent.\"\"\"\n # Convert AG-UI messages to SuperOptiX format\n messages_data = [message.model_dump() for message in messages]\n \n # Remove system message if present\n if len(messages_data) > 0:\n if \"role\" in messages_data[0] and messages_data[0][\"role\"] == \"system\":\n messages_data = messages[1:]\n \n # Convert tools to SuperOptiX format\n tools_data = [tool.model_dump() for tool in tools]\n \n # Prepare state for SuperOptiX\n new_state = {\n **state,\n \"messages\": messages_data,\n \"tools\": tools_data\n }\n \n return new_state\n\n\ndef add_superoptix_fastapi_endpoint(\n app: FastAPI, \n agent_name: str, \n project_root: Optional[Path] = None,\n path: str = \"/\"\n):\n \"\"\"Adds a SuperOptiX endpoint to the FastAPI app.\"\"\"\n \n @app.post(path)\n async def agentic_chat_endpoint(input_data: RunAgentInput, request: Request):\n \"\"\"SuperOptiX agentic chat endpoint\"\"\"\n \n # Initialize SuperOptiX runner\n runner = DSPyRunner(\n agent_name=agent_name,\n project_root=project_root\n )\n \n # Get the accept header from the request\n accept_header = request.headers.get(\"accept\")\n \n # Create an event encoder to properly format SSE events\n encoder = EventEncoder(accept=accept_header)\n \n # Prepare inputs for SuperOptiX\n inputs = superoptix_prepare_inputs(\n state=input_data.state,\n messages=input_data.messages,\n tools=input_data.tools,\n )\n \n # Extract the main query from messages\n query = \"\"\n if input_data.messages:\n # Get the last user message as the query\n for message in reversed(input_data.messages):\n if message.role == \"user\":\n query = message.content or \"\"\n break\n \n async def event_generator():\n try:\n # Send run started event\n yield encoder.encode(\n RunStartedEvent(\n type=EventType.RUN_STARTED,\n thread_id=input_data.thread_id,\n run_id=input_data.run_id\n )\n )\n \n # Send step started event\n step_id = str(uuid.uuid4())\n yield encoder.encode(\n StepStartedEvent(\n type=EventType.STEP_STARTED,\n step_id=step_id,\n name=\"superoptix_agent_execution\"\n )\n )\n \n # Run the SuperOptiX agent\n result = await runner.run(query=query, **inputs)\n \n # Send step finished event\n yield encoder.encode(\n StepFinishedEvent(\n type=EventType.STEP_FINISHED,\n step_id=step_id\n )\n )\n \n # Send the result as a text message\n message_id = str(uuid.uuid4())\n \n # Start message\n yield encoder.encode({\n \"type\": EventType.TEXT_MESSAGE_START,\n \"message_id\": message_id,\n \"role\": \"assistant\"\n })\n \n # Send content in chunks\n if isinstance(result, dict):\n content = result.get(\"implementation\", str(result))\n else:\n content = str(result)\n \n # Split content into chunks for streaming\n chunk_size = 50\n for i in range(0, len(content), chunk_size):\n chunk = content[i:i + chunk_size]\n yield encoder.encode({\n \"type\": EventType.TEXT_MESSAGE_CHUNK,\n \"message_id\": message_id,\n \"delta\": chunk\n })\n \n # End message\n yield encoder.encode({\n \"type\": EventType.TEXT_MESSAGE_END,\n \"message_id\": message_id\n })\n \n # Send run finished event\n yield encoder.encode(\n RunFinishedEvent(\n type=EventType.RUN_FINISHED,\n thread_id=input_data.thread_id,\n run_id=input_data.run_id\n )\n )\n \n except Exception as error:\n yield encoder.encode(\n RunErrorEvent(\n type=EventType.RUN_ERROR,\n thread_id=input_data.thread_id,\n run_id=input_data.run_id,\n error=str(error)\n )\n )\n \n return StreamingResponse(\n event_generator(),\n media_type=encoder.get_content_type()\n ) ", + "language": "python", + "type": "file" + } + ], + "superoptix::human_in_the_loop": [ + { + "name": "page.tsx", + "content": "\"use client\";\nimport React, { useState, useEffect } from \"react\";\nimport \"@copilotkit/react-ui/styles.css\";\nimport \"./style.css\";\nimport { CopilotKit, useCopilotAction, useLangGraphInterrupt } from \"@copilotkit/react-core\";\nimport { CopilotChat } from \"@copilotkit/react-ui\";\nimport { useTheme } from \"next-themes\";\n\ninterface HumanInTheLoopProps {\n params: Promise<{\n integrationId: string;\n }>;\n}\n\nconst HumanInTheLoop: React.FC = ({ params }) => {\n const { integrationId } = React.use(params);\n\n return (\n \n \n \n );\n};\n\ninterface Step {\n description: string;\n status: \"disabled\" | \"enabled\" | \"executing\";\n}\n\n// Shared UI Components\nconst StepContainer = ({ theme, children }: { theme?: string; children: React.ReactNode }) => (\n
\n
\n {children}\n
\n
\n);\n\nconst StepHeader = ({ \n theme, \n enabledCount, \n totalCount, \n status, \n showStatus = false \n}: { \n theme?: string; \n enabledCount: number; \n totalCount: number; \n status?: string;\n showStatus?: boolean;\n}) => (\n
\n
\n

\n Select Steps\n

\n
\n
\n {enabledCount}/{totalCount} Selected\n
\n {showStatus && (\n
\n {status === \"executing\" ? \"Ready\" : \"Waiting\"}\n
\n )}\n
\n
\n \n
\n
0 ? (enabledCount / totalCount) * 100 : 0}%` }}\n />\n
\n
\n);\n\nconst StepItem = ({ \n step, \n theme, \n status, \n onToggle, \n disabled = false \n}: { \n step: { description: string; status: string }; \n theme?: string; \n status?: string;\n onToggle: () => void;\n disabled?: boolean;\n}) => (\n
\n \n
\n);\n\nconst ActionButton = ({ \n variant, \n theme, \n disabled, \n onClick, \n children \n}: { \n variant: \"primary\" | \"secondary\" | \"success\" | \"danger\";\n theme?: string;\n disabled?: boolean;\n onClick: () => void;\n children: React.ReactNode;\n}) => {\n const baseClasses = \"px-6 py-3 rounded-lg font-semibold transition-all duration-200\";\n const enabledClasses = \"hover:scale-105 shadow-md hover:shadow-lg\";\n const disabledClasses = \"opacity-50 cursor-not-allowed\";\n \n const variantClasses = {\n primary: \"bg-gradient-to-r from-purple-500 to-purple-700 hover:from-purple-600 hover:to-purple-800 text-white shadow-lg hover:shadow-xl\",\n secondary: theme === \"dark\"\n ? \"bg-slate-700 hover:bg-slate-600 text-white border border-slate-600 hover:border-slate-500\"\n : \"bg-gray-100 hover:bg-gray-200 text-gray-800 border border-gray-300 hover:border-gray-400\",\n success: \"bg-gradient-to-r from-green-500 to-emerald-600 hover:from-green-600 hover:to-emerald-700 text-white shadow-lg hover:shadow-xl\",\n danger: \"bg-gradient-to-r from-red-500 to-red-600 hover:from-red-600 hover:to-red-700 text-white shadow-lg hover:shadow-xl\"\n };\n\n return (\n \n {children}\n \n );\n};\n\nconst DecorativeElements = ({ \n theme, \n variant = \"default\" \n}: { \n theme?: string; \n variant?: \"default\" | \"success\" | \"danger\" \n}) => (\n <>\n
\n
\n \n);\nconst InterruptHumanInTheLoop: React.FC<{\n event: { value: { steps: Step[] } };\n resolve: (value: string) => void;\n}> = ({ event, resolve }) => {\n const { theme } = useTheme();\n \n // Parse and initialize steps data\n let initialSteps: Step[] = [];\n if (event.value && event.value.steps && Array.isArray(event.value.steps)) {\n initialSteps = event.value.steps.map((step: any) => ({\n description: typeof step === \"string\" ? step : step.description || \"\",\n status: typeof step === \"object\" && step.status ? step.status : \"enabled\",\n }));\n }\n\n const [localSteps, setLocalSteps] = useState(initialSteps);\n const enabledCount = localSteps.filter(step => step.status === \"enabled\").length;\n\n const handleStepToggle = (index: number) => {\n setLocalSteps((prevSteps) =>\n prevSteps.map((step, i) =>\n i === index\n ? { ...step, status: step.status === \"enabled\" ? \"disabled\" : \"enabled\" }\n : step,\n ),\n );\n };\n\n const handlePerformSteps = () => {\n const selectedSteps = localSteps\n .filter((step) => step.status === \"enabled\")\n .map((step) => step.description);\n resolve(\"The user selected the following steps: \" + selectedSteps.join(\", \"));\n };\n\n return (\n \n \n \n
\n {localSteps.map((step, index) => (\n handleStepToggle(index)}\n />\n ))}\n
\n\n
\n \n \n Perform Steps\n \n {enabledCount}\n \n \n
\n\n \n
\n );\n};\n\nconst Chat = ({ integrationId }: { integrationId: string }) => {\n // Langgraph uses it's own hook to handle human-in-the-loop interactions via langgraph interrupts,\n // This hook won't do anything for other integrations.\n useLangGraphInterrupt({\n render: ({ event, resolve }) => ,\n });\n useCopilotAction({\n name: \"generate_task_steps\",\n description: \"Generates a list of steps for the user to perform\",\n parameters: [\n {\n name: \"steps\",\n type: \"object[]\",\n attributes: [\n {\n name: \"description\",\n type: \"string\",\n },\n {\n name: \"status\",\n type: \"string\",\n enum: [\"enabled\", \"disabled\", \"executing\"],\n },\n ],\n },\n ],\n // Langgraph uses it's own hook to handle human-in-the-loop interactions via langgraph interrupts,\n // so don't use this action for langgraph integration.\n available: ['langgraph', 'langgraph-fastapi'].includes(integrationId) ? 'disabled' : 'enabled',\n renderAndWaitForResponse: ({ args, respond, status }) => {\n return ;\n },\n });\n\n return (\n
\n
\n \n
\n
\n );\n};\n\nconst StepsFeedback = ({ args, respond, status }: { args: any; respond: any; status: any }) => {\n const { theme } = useTheme();\n const [localSteps, setLocalSteps] = useState([]);\n const [accepted, setAccepted] = useState(null);\n\n useEffect(() => {\n if (status === \"executing\" && localSteps.length === 0) {\n setLocalSteps(args.steps);\n }\n }, [status, args.steps, localSteps]);\n\n if (args.steps === undefined || args.steps.length === 0) {\n return <>;\n }\n\n const steps = localSteps.length > 0 ? localSteps : args.steps;\n const enabledCount = steps.filter((step: any) => step.status === \"enabled\").length;\n\n const handleStepToggle = (index: number) => {\n setLocalSteps((prevSteps) =>\n prevSteps.map((step, i) =>\n i === index\n ? { ...step, status: step.status === \"enabled\" ? \"disabled\" : \"enabled\" }\n : step,\n ),\n );\n };\n\n const handleReject = () => {\n if (respond) {\n setAccepted(false);\n respond({ accepted: false });\n }\n };\n\n const handleConfirm = () => {\n if (respond) {\n setAccepted(true);\n respond({ accepted: true, steps: localSteps.filter(step => step.status === \"enabled\")});\n }\n };\n\n return (\n \n \n \n
\n {steps.map((step: any, index: any) => (\n handleStepToggle(index)}\n disabled={status !== \"executing\"}\n />\n ))}\n
\n\n {/* Action Buttons - Different logic from InterruptHumanInTheLoop */}\n {accepted === null && (\n
\n \n \n Reject\n \n \n \n Confirm\n \n {enabledCount}\n \n \n
\n )}\n\n {/* Result State - Unique to StepsFeedback */}\n {accepted !== null && (\n
\n
\n {accepted ? \"✓\" : \"✗\"}\n {accepted ? \"Accepted\" : \"Rejected\"}\n
\n
\n )}\n\n \n
\n );\n};\n\n\nexport default HumanInTheLoop;\n", + "language": "typescript", + "type": "file" + }, + { + "name": "style.css", + "content": ".copilotKitInput {\n border-bottom-left-radius: 0.75rem;\n border-bottom-right-radius: 0.75rem;\n border-top-left-radius: 0.75rem;\n border-top-right-radius: 0.75rem;\n border: 1px solid var(--copilot-kit-separator-color) !important;\n}\n\n.copilotKitChat {\n background-color: #fff !important;\n}\n", + "language": "css", + "type": "file" + }, + { + "name": "README.mdx", + "content": "# 🤝 Human-in-the-Loop Task Planner\n\n## What This Demo Shows\n\nThis demo showcases CopilotKit's **human-in-the-loop** capabilities:\n\n1. **Collaborative Planning**: The Copilot generates task steps and lets you\n decide which ones to perform\n2. **Interactive Decision Making**: Select or deselect steps to customize the\n execution plan\n3. **Adaptive Responses**: The Copilot adapts its execution based on your\n choices, even handling missing steps\n\n## How to Interact\n\nTry these steps to experience the demo:\n\n1. Ask your Copilot to help with a task, such as:\n\n - \"Make me a sandwich\"\n - \"Plan a weekend trip\"\n - \"Organize a birthday party\"\n - \"Start a garden\"\n\n2. Review the suggested steps provided by your Copilot\n\n3. Select or deselect steps using the checkboxes to customize the plan\n\n - Try removing essential steps to see how the Copilot adapts!\n\n4. Click \"Execute Plan\" to see the outcome based on your selections\n\n## ✨ Human-in-the-Loop Magic in Action\n\n**What's happening technically:**\n\n- The agent analyzes your request and breaks it down into logical steps\n- These steps are presented to you through a dynamic UI component\n- Your selections are captured as user input\n- The agent considers your choices when executing the plan\n- The agent adapts to missing steps with creative problem-solving\n\n**What you'll see in this demo:**\n\n- The Copilot provides a detailed, step-by-step plan for your task\n- You have complete control over which steps to include\n- If you remove essential steps, the Copilot provides entertaining and creative\n workarounds\n- The final execution reflects your choices, showing how human input shapes the\n outcome\n- Each response is tailored to your specific selections\n\nThis human-in-the-loop pattern creates a powerful collaborative experience where\nboth human judgment and AI capabilities work together to achieve better results\nthan either could alone!\n", + "language": "markdown", + "type": "file" + }, + { + "name": "endpoint.py", + "content": "\"\"\"\nAG-UI FastAPI server for SuperOptiX.\n\"\"\"\nimport asyncio\nimport uuid\nfrom typing import List, Optional, Dict, Any\nfrom pathlib import Path\n\nfrom fastapi import FastAPI, Request\nfrom fastapi.responses import StreamingResponse\n\nfrom ag_ui.core import (\n RunAgentInput,\n EventType,\n RunStartedEvent,\n RunFinishedEvent,\n RunErrorEvent,\n Message,\n Tool\n)\nfrom ag_ui.core.events import (\n TextMessageChunkEvent,\n ToolCallChunkEvent,\n StepStartedEvent,\n StepFinishedEvent,\n MessagesSnapshotEvent,\n StateSnapshotEvent,\n CustomEvent,\n)\nfrom ag_ui.encoder import EventEncoder\n\nfrom superoptix.runners.dspy_runner import DSPyRunner\n\nQUEUES = {}\nQUEUES_LOCK = asyncio.Lock()\n\n\nasync def create_queue(runner: DSPyRunner) -> asyncio.Queue:\n \"\"\"Create a queue for a runner.\"\"\"\n queue_id = id(runner)\n async with QUEUES_LOCK:\n queue = asyncio.Queue()\n QUEUES[queue_id] = queue\n return queue\n\n\ndef get_queue(runner: DSPyRunner) -> Optional[asyncio.Queue]:\n \"\"\"Get the queue for a runner.\"\"\"\n queue_id = id(runner)\n return QUEUES.get(queue_id)\n\n\nasync def delete_queue(runner: DSPyRunner) -> None:\n \"\"\"Delete the queue for a runner.\"\"\"\n queue_id = id(runner)\n async with QUEUES_LOCK:\n if queue_id in QUEUES:\n del QUEUES[queue_id]\n\n\ndef superoptix_prepare_inputs(\n *,\n state: dict,\n messages: List[Message],\n tools: List[Tool],\n) -> Dict[str, Any]:\n \"\"\"Prepare inputs for SuperOptiX agent.\"\"\"\n # Convert AG-UI messages to SuperOptiX format\n messages_data = [message.model_dump() for message in messages]\n \n # Remove system message if present\n if len(messages_data) > 0:\n if \"role\" in messages_data[0] and messages_data[0][\"role\"] == \"system\":\n messages_data = messages[1:]\n \n # Convert tools to SuperOptiX format\n tools_data = [tool.model_dump() for tool in tools]\n \n # Prepare state for SuperOptiX\n new_state = {\n **state,\n \"messages\": messages_data,\n \"tools\": tools_data\n }\n \n return new_state\n\n\ndef add_superoptix_fastapi_endpoint(\n app: FastAPI, \n agent_name: str, \n project_root: Optional[Path] = None,\n path: str = \"/\"\n):\n \"\"\"Adds a SuperOptiX endpoint to the FastAPI app.\"\"\"\n \n @app.post(path)\n async def agentic_chat_endpoint(input_data: RunAgentInput, request: Request):\n \"\"\"SuperOptiX agentic chat endpoint\"\"\"\n \n # Initialize SuperOptiX runner\n runner = DSPyRunner(\n agent_name=agent_name,\n project_root=project_root\n )\n \n # Get the accept header from the request\n accept_header = request.headers.get(\"accept\")\n \n # Create an event encoder to properly format SSE events\n encoder = EventEncoder(accept=accept_header)\n \n # Prepare inputs for SuperOptiX\n inputs = superoptix_prepare_inputs(\n state=input_data.state,\n messages=input_data.messages,\n tools=input_data.tools,\n )\n \n # Extract the main query from messages\n query = \"\"\n if input_data.messages:\n # Get the last user message as the query\n for message in reversed(input_data.messages):\n if message.role == \"user\":\n query = message.content or \"\"\n break\n \n async def event_generator():\n try:\n # Send run started event\n yield encoder.encode(\n RunStartedEvent(\n type=EventType.RUN_STARTED,\n thread_id=input_data.thread_id,\n run_id=input_data.run_id\n )\n )\n \n # Send step started event\n step_id = str(uuid.uuid4())\n yield encoder.encode(\n StepStartedEvent(\n type=EventType.STEP_STARTED,\n step_id=step_id,\n name=\"superoptix_agent_execution\"\n )\n )\n \n # Run the SuperOptiX agent\n result = await runner.run(query=query, **inputs)\n \n # Send step finished event\n yield encoder.encode(\n StepFinishedEvent(\n type=EventType.STEP_FINISHED,\n step_id=step_id\n )\n )\n \n # Send the result as a text message\n message_id = str(uuid.uuid4())\n \n # Start message\n yield encoder.encode({\n \"type\": EventType.TEXT_MESSAGE_START,\n \"message_id\": message_id,\n \"role\": \"assistant\"\n })\n \n # Send content in chunks\n if isinstance(result, dict):\n content = result.get(\"implementation\", str(result))\n else:\n content = str(result)\n \n # Split content into chunks for streaming\n chunk_size = 50\n for i in range(0, len(content), chunk_size):\n chunk = content[i:i + chunk_size]\n yield encoder.encode({\n \"type\": EventType.TEXT_MESSAGE_CHUNK,\n \"message_id\": message_id,\n \"delta\": chunk\n })\n \n # End message\n yield encoder.encode({\n \"type\": EventType.TEXT_MESSAGE_END,\n \"message_id\": message_id\n })\n \n # Send run finished event\n yield encoder.encode(\n RunFinishedEvent(\n type=EventType.RUN_FINISHED,\n thread_id=input_data.thread_id,\n run_id=input_data.run_id\n )\n )\n \n except Exception as error:\n yield encoder.encode(\n RunErrorEvent(\n type=EventType.RUN_ERROR,\n thread_id=input_data.thread_id,\n run_id=input_data.run_id,\n error=str(error)\n )\n )\n \n return StreamingResponse(\n event_generator(),\n media_type=encoder.get_content_type()\n ) ", + "language": "python", + "type": "file" + } + ], + "superoptix::tool_based_generative_ui": [ + { + "name": "page.tsx", + "content": "\"use client\";\nimport { CopilotKit, useCopilotAction } from \"@copilotkit/react-core\";\nimport { CopilotKitCSSProperties, CopilotSidebar, CopilotChat } from \"@copilotkit/react-ui\";\nimport { Dispatch, SetStateAction, useState, useEffect } from \"react\";\nimport \"@copilotkit/react-ui/styles.css\";\nimport \"./style.css\";\nimport React, { useMemo } from \"react\";\nimport { useMobileView } from \"@/utils/use-mobile-view\";\nimport { useMobileChat } from \"@/utils/use-mobile-chat\";\n\ninterface ToolBasedGenerativeUIProps {\n params: Promise<{\n integrationId: string;\n }>;\n}\n\ninterface GenerateHaiku{\n japanese : string[] | [],\n english : string[] | [],\n image_names : string[] | [],\n selectedImage : string | null,\n}\n\ninterface HaikuCardProps{\n generatedHaiku : GenerateHaiku | Partial\n setHaikus : Dispatch>\n haikus : GenerateHaiku[]\n}\n\nexport default function ToolBasedGenerativeUI({ params }: ToolBasedGenerativeUIProps) {\n const { integrationId } = React.use(params);\n const { isMobile } = useMobileView();\n const defaultChatHeight = 50\n const {\n isChatOpen,\n setChatHeight,\n setIsChatOpen,\n isDragging,\n chatHeight,\n handleDragStart\n } = useMobileChat(defaultChatHeight)\n\n const chatTitle = 'Haiku Generator'\n const chatDescription = 'Ask me to create haikus'\n const initialLabel = 'I\\'m a haiku generator 👋. How can I help you?'\n\n return (\n \n \n \n\n {/* Desktop Sidebar */}\n {!isMobile && (\n \n )}\n\n {/* Mobile Pull-Up Chat */}\n {isMobile && (\n <>\n {/* Chat Toggle Button */}\n
\n
\n {\n if (!isChatOpen) {\n setChatHeight(defaultChatHeight); // Reset to good default when opening\n }\n setIsChatOpen(!isChatOpen);\n }}\n >\n
\n
\n
{chatTitle}
\n
{chatDescription}
\n
\n
\n
\n \n \n \n
\n
\n
\n\n {/* Pull-Up Chat Container */}\n \n {/* Drag Handle Bar */}\n
\n
\n
\n \n {/* Chat Header */}\n
\n
\n
\n

{chatTitle}

\n
\n setIsChatOpen(false)}\n className=\"p-2 hover:bg-gray-100 rounded-full transition-colors\"\n >\n \n \n \n \n
\n
\n\n {/* Chat Content - Flexible container for messages and input */}\n
\n \n
\n
\n\n {/* Backdrop */}\n {isChatOpen && (\n setIsChatOpen(false)}\n />\n )}\n \n )}\n
\n \n );\n}\n\nconst VALID_IMAGE_NAMES = [\n \"Osaka_Castle_Turret_Stone_Wall_Pine_Trees_Daytime.jpg\",\n \"Tokyo_Skyline_Night_Tokyo_Tower_Mount_Fuji_View.jpg\",\n \"Itsukushima_Shrine_Miyajima_Floating_Torii_Gate_Sunset_Long_Exposure.jpg\",\n \"Takachiho_Gorge_Waterfall_River_Lush_Greenery_Japan.jpg\",\n \"Bonsai_Tree_Potted_Japanese_Art_Green_Foliage.jpeg\",\n \"Shirakawa-go_Gassho-zukuri_Thatched_Roof_Village_Aerial_View.jpg\",\n \"Ginkaku-ji_Silver_Pavilion_Kyoto_Japanese_Garden_Pond_Reflection.jpg\",\n \"Senso-ji_Temple_Asakusa_Cherry_Blossoms_Kimono_Umbrella.jpg\",\n \"Cherry_Blossoms_Sakura_Night_View_City_Lights_Japan.jpg\",\n \"Mount_Fuji_Lake_Reflection_Cherry_Blossoms_Sakura_Spring.jpg\"\n];\n\nfunction HaikuCard({generatedHaiku, setHaikus, haikus} : HaikuCardProps) {\n return (\n
\n
\n {generatedHaiku?.japanese?.map((line, index) => (\n
\n

{line}

\n

\n {generatedHaiku.english?.[index]}\n

\n
\n ))}\n {generatedHaiku?.japanese && generatedHaiku.japanese.length >= 2 && (\n
\n {(() => {\n const firstLine = generatedHaiku?.japanese?.[0];\n if (!firstLine) return null;\n const haikuIndex = haikus.findIndex((h: any) => h.japanese[0] === firstLine);\n const haiku = haikus[haikuIndex];\n if (!haiku?.image_names) return null;\n\n return haiku.image_names.map((imageName, imgIndex) => (\n {\n setHaikus(prevHaikus => {\n const newHaikus = prevHaikus.map((h, idx) => {\n if (idx === haikuIndex) {\n return {\n ...h,\n selectedImage: imageName\n };\n }\n return h;\n });\n return newHaikus;\n });\n }}\n />\n ));\n })()}\n
\n )}\n
\n
\n );\n}\n\ninterface Haiku {\n japanese: string[];\n english: string[];\n image_names: string[];\n selectedImage: string | null;\n}\n\nfunction Haiku() {\n const [haikus, setHaikus] = useState([{\n japanese: [\"仮の句よ\", \"まっさらながら\", \"花を呼ぶ\"],\n english: [\n \"A placeholder verse—\",\n \"even in a blank canvas,\",\n \"it beckons flowers.\",\n ],\n image_names: [],\n selectedImage: null,\n }])\n const [activeIndex, setActiveIndex] = useState(0);\n const [isJustApplied, setIsJustApplied] = useState(false);\n\n const validateAndCorrectImageNames = (rawNames: string[] | undefined): string[] | null => {\n if (!rawNames || rawNames.length !== 3) {\n return null;\n }\n\n const correctedNames: string[] = [];\n const usedValidNames = new Set();\n\n for (const name of rawNames) {\n if (VALID_IMAGE_NAMES.includes(name) && !usedValidNames.has(name)) {\n correctedNames.push(name);\n usedValidNames.add(name);\n if (correctedNames.length === 3) break;\n }\n }\n\n if (correctedNames.length < 3) {\n const availableFallbacks = VALID_IMAGE_NAMES.filter(name => !usedValidNames.has(name));\n for (let i = availableFallbacks.length - 1; i > 0; i--) {\n const j = Math.floor(Math.random() * (i + 1));\n [availableFallbacks[i], availableFallbacks[j]] = [availableFallbacks[j], availableFallbacks[i]];\n }\n\n while (correctedNames.length < 3 && availableFallbacks.length > 0) {\n const fallbackName = availableFallbacks.pop();\n if (fallbackName) {\n correctedNames.push(fallbackName);\n }\n }\n }\n\n while (correctedNames.length < 3 && VALID_IMAGE_NAMES.length > 0) {\n const fallbackName = VALID_IMAGE_NAMES[Math.floor(Math.random() * VALID_IMAGE_NAMES.length)];\n correctedNames.push(fallbackName);\n }\n\n return correctedNames.slice(0, 3);\n };\n\n useCopilotAction({\n name: \"generate_haiku\",\n parameters: [\n {\n name: \"japanese\",\n type: \"string[]\",\n },\n {\n name: \"english\",\n type: \"string[]\",\n },\n {\n name: \"image_names\",\n type: \"string[]\",\n description: \"Names of 3 relevant images\",\n },\n ],\n followUp: false,\n handler: async ({ japanese, english, image_names }: { japanese: string[], english: string[], image_names: string[] }) => {\n const finalCorrectedImages = validateAndCorrectImageNames(image_names);\n const newHaiku = {\n japanese: japanese || [],\n english: english || [],\n image_names: finalCorrectedImages || [],\n selectedImage: finalCorrectedImages?.[0] || null,\n };\n setHaikus(prev => [...prev, newHaiku]);\n setActiveIndex(haikus.length - 1);\n setIsJustApplied(true);\n setTimeout(() => setIsJustApplied(false), 600);\n return \"Haiku generated.\";\n },\n render: ({ args: generatedHaiku }: { args: Partial }) => {\n return (\n \n );\n },\n }, [haikus]);\n\n const generatedHaikus = useMemo(() => (\n haikus.filter((haiku) => haiku.english[0] !== \"A placeholder verse—\")\n ), [haikus]);\n\n const { isMobile } = useMobileView();\n\n return (\n
\n {/* Thumbnail List */}\n {Boolean(generatedHaikus.length) && !isMobile && (\n
\n {generatedHaikus.map((haiku, index) => (\n setActiveIndex(index)}\n >\n {haiku.japanese.map((line, lineIndex) => (\n \n

{line}

\n

{haiku.english?.[lineIndex]}

\n
\n ))}\n {haiku.image_names && haiku.image_names.length === 3 && (\n
\n {haiku.image_names.map((imageName, imgIndex) => (\n \n ))}\n
\n )}\n
\n ))}\n \n )}\n\n {/* Main Display */}\n
\n
\n {haikus.filter((_haiku: Haiku, index: number) => {\n if (haikus.length == 1) return true;\n else return index == activeIndex + 1;\n }).map((haiku, index) => (\n \n {haiku.japanese.map((line, lineIndex) => (\n \n

\n {line}\n

\n

\n {haiku.english?.[lineIndex]}\n

\n
\n ))}\n {haiku.image_names && haiku.image_names.length === 3 && (\n
\n {haiku.image_names.map((imageName, imgIndex) => (\n \n ))}\n
\n )}\n
\n ))}\n \n \n \n );\n}\n", + "language": "typescript", + "type": "file" + }, + { + "name": "style.css", + "content": ".copilotKitWindow {\n box-shadow: 0 2px 10px rgba(0, 0, 0, 0.1);\n}\n\n.copilotKitHeader {\n border-top-left-radius: 5px !important;\n}\n\n.page-background {\n /* Darker gradient background */\n background: linear-gradient(170deg, #e9ecef 0%, #ced4da 100%);\n}\n\n@keyframes fade-scale-in {\n from {\n opacity: 0;\n transform: translateY(10px) scale(0.98);\n }\n to {\n opacity: 1;\n transform: translateY(0) scale(1);\n }\n}\n\n/* Updated card entry animation */\n@keyframes pop-in {\n 0% {\n opacity: 0;\n transform: translateY(15px) scale(0.95);\n }\n 70% {\n opacity: 1;\n transform: translateY(-2px) scale(1.02);\n }\n 100% {\n opacity: 1;\n transform: translateY(0) scale(1);\n }\n}\n\n/* Animation for subtle background gradient movement */\n@keyframes animated-gradient {\n 0% {\n background-position: 0% 50%;\n }\n 50% {\n background-position: 100% 50%;\n }\n 100% {\n background-position: 0% 50%;\n }\n}\n\n/* Animation for flash effect on apply */\n@keyframes flash-border-glow {\n 0% {\n /* Start slightly intensified */\n border-top-color: #ff5b4a !important;\n box-shadow: 0 10px 30px rgba(0, 0, 0, 0.07),\n inset 0 1px 2px rgba(0, 0, 0, 0.01),\n 0 0 25px rgba(255, 91, 74, 0.5);\n }\n 50% {\n /* Peak intensity */\n border-top-color: #ff4733 !important;\n box-shadow: 0 10px 30px rgba(0, 0, 0, 0.08),\n inset 0 1px 2px rgba(0, 0, 0, 0.01),\n 0 0 35px rgba(255, 71, 51, 0.7);\n }\n 100% {\n /* Return to default state appearance */\n border-top-color: #ff6f61 !important;\n box-shadow: 0 10px 30px rgba(0, 0, 0, 0.07),\n inset 0 1px 2px rgba(0, 0, 0, 0.01),\n 0 0 10px rgba(255, 111, 97, 0.15);\n }\n}\n\n/* Existing animation for haiku lines */\n@keyframes fade-slide-in {\n from {\n opacity: 0;\n transform: translateX(-15px);\n }\n to {\n opacity: 1;\n transform: translateX(0);\n }\n}\n\n.animated-fade-in {\n /* Use the new pop-in animation */\n animation: pop-in 0.6s ease-out forwards;\n}\n\n.haiku-card {\n /* Subtle animated gradient background */\n background: linear-gradient(120deg, #ffffff 0%, #fdfdfd 50%, #ffffff 100%);\n background-size: 200% 200%;\n animation: animated-gradient 10s ease infinite;\n\n /* === Explicit Border Override Attempt === */\n /* 1. Set the default grey border for all sides */\n border: 1px solid #dee2e6;\n\n /* 2. Explicitly override the top border immediately after */\n border-top: 10px solid #ff6f61 !important; /* Orange top - Added !important */\n /* === End Explicit Border Override Attempt === */\n\n padding: 2.5rem 3rem;\n border-radius: 20px;\n\n /* Default glow intensity */\n box-shadow: 0 10px 30px rgba(0, 0, 0, 0.07),\n inset 0 1px 2px rgba(0, 0, 0, 0.01),\n 0 0 15px rgba(255, 111, 97, 0.25);\n text-align: left;\n max-width: 745px;\n margin: 3rem auto;\n min-width: 600px;\n\n /* Transition */\n transition: transform 0.35s ease, box-shadow 0.35s ease, border-top-width 0.35s ease, border-top-color 0.35s ease;\n}\n\n.haiku-card:hover {\n transform: translateY(-8px) scale(1.03);\n /* Enhanced shadow + Glow */\n box-shadow: 0 15px 35px rgba(0, 0, 0, 0.1),\n inset 0 1px 2px rgba(0, 0, 0, 0.01),\n 0 0 25px rgba(255, 91, 74, 0.5);\n /* Modify only top border properties */\n border-top-width: 14px !important; /* Added !important */\n border-top-color: #ff5b4a !important; /* Added !important */\n}\n\n.haiku-card .flex {\n margin-bottom: 1.5rem;\n}\n\n.haiku-card .flex.haiku-line { /* Target the lines specifically */\n margin-bottom: 1.5rem;\n opacity: 0; /* Start hidden for animation */\n animation: fade-slide-in 0.5s ease-out forwards;\n /* animation-delay is set inline in page.tsx */\n}\n\n/* Remove previous explicit color overrides - rely on Tailwind */\n/* .haiku-card p.text-4xl {\n color: #212529;\n}\n\n.haiku-card p.text-base {\n color: #495057;\n} */\n\n.haiku-card.applied-flash {\n /* Apply the flash animation once */\n /* Note: animation itself has !important on border-top-color */\n animation: flash-border-glow 0.6s ease-out forwards;\n}\n\n/* Styling for images within the main haiku card */\n.haiku-card-image {\n width: 9.5rem; /* Increased size (approx w-48) */\n height: 9.5rem; /* Increased size (approx h-48) */\n object-fit: cover;\n border-radius: 1.5rem; /* rounded-xl */\n border: 1px solid #e5e7eb;\n /* Enhanced shadow with subtle orange hint */\n box-shadow: 0 8px 15px rgba(0, 0, 0, 0.1),\n 0 3px 6px rgba(0, 0, 0, 0.08),\n 0 0 10px rgba(255, 111, 97, 0.2);\n /* Inherit animation delay from inline style */\n animation-name: fadeIn;\n animation-duration: 0.5s;\n animation-fill-mode: both;\n}\n\n/* Styling for images within the suggestion card */\n.suggestion-card-image {\n width: 6.5rem; /* Increased slightly (w-20) */\n height: 6.5rem; /* Increased slightly (h-20) */\n object-fit: cover;\n border-radius: 1rem; /* Equivalent to rounded-md */\n border: 1px solid #d1d5db; /* Equivalent to border (using Tailwind gray-300) */\n margin-top: 0.5rem;\n /* Added shadow for suggestion images */\n box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1),\n 0 2px 4px rgba(0, 0, 0, 0.06);\n transition: all 0.2s ease-in-out; /* Added for smooth deselection */\n}\n\n/* Styling for the focused suggestion card image */\n.suggestion-card-image-focus {\n width: 6.5rem;\n height: 6.5rem;\n object-fit: cover;\n border-radius: 1rem;\n margin-top: 0.5rem;\n /* Highlight styles */\n border: 2px solid #ff6f61; /* Thicker, themed border */\n box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1), /* Base shadow for depth */\n 0 0 12px rgba(255, 111, 97, 0.6); /* Orange glow */\n transform: scale(1.05); /* Slightly scale up */\n transition: all 0.2s ease-in-out; /* Smooth transition for focus */\n}\n\n/* Styling for the suggestion card container in the sidebar */\n.suggestion-card {\n border: 1px solid #dee2e6; /* Same default border as haiku-card */\n border-top: 10px solid #ff6f61; /* Same orange top border */\n border-radius: 0.375rem; /* Default rounded-md */\n /* Note: background-color is set by Tailwind bg-gray-100 */\n /* Other styles like padding, margin, flex are handled by Tailwind */\n}\n\n.suggestion-image-container {\n display: flex;\n gap: 1rem;\n justify-content: space-between;\n width: 100%;\n height: 6.5rem;\n}\n\n/* Mobile responsive styles - matches useMobileView hook breakpoint */\n@media (max-width: 767px) {\n .haiku-card {\n padding: 1rem 1.5rem; /* Reduced from 2.5rem 3rem */\n min-width: auto; /* Remove min-width constraint */\n max-width: 100%; /* Full width on mobile */\n margin: 1rem auto; /* Reduced margin */\n }\n\n .haiku-card-image {\n width: 5.625rem; /* 90px - smaller on mobile */\n height: 5.625rem; /* 90px - smaller on mobile */\n }\n\n .suggestion-card-image {\n width: 5rem; /* Slightly smaller on mobile */\n height: 5rem; /* Slightly smaller on mobile */\n }\n\n .suggestion-card-image-focus {\n width: 5rem; /* Slightly smaller on mobile */\n height: 5rem; /* Slightly smaller on mobile */\n }\n}\n", + "language": "css", + "type": "file" + }, + { + "name": "README.mdx", + "content": "# 🪶 Tool-Based Generative UI Haiku Creator\n\n## What This Demo Shows\n\nThis demo showcases CopilotKit's **tool-based generative UI** capabilities:\n\n1. **Frontend Rendering of Tool Calls**: Backend tool calls are automatically\n rendered in the UI\n2. **Dynamic UI Generation**: The UI updates in real-time as the agent generates\n content\n3. **Elegant Content Presentation**: Complex structured data (haikus) are\n beautifully displayed\n\n## How to Interact\n\nChat with your Copilot and ask for haikus about different topics:\n\n- \"Create a haiku about nature\"\n- \"Write a haiku about technology\"\n- \"Generate a haiku about the changing seasons\"\n- \"Make a humorous haiku about programming\"\n\nEach request will trigger the agent to generate a haiku and display it in a\nvisually appealing card format in the UI.\n\n## ✨ Tool-Based Generative UI in Action\n\n**What's happening technically:**\n\n- The agent processes your request and determines it should create a haiku\n- It calls a backend tool that returns structured haiku data\n- CopilotKit automatically renders this tool call in the frontend\n- The rendering is handled by the registered tool component in your React app\n- No manual state management is required to display the results\n\n**What you'll see in this demo:**\n\n- As you request a haiku, a beautifully formatted card appears in the UI\n- The haiku follows the traditional 5-7-5 syllable structure\n- Each haiku is presented with consistent styling\n- Multiple haikus can be generated in sequence\n- The UI adapts to display each new piece of content\n\nThis pattern of tool-based generative UI can be extended to create any kind of\ndynamic content - from data visualizations to interactive components, all driven\nby your Copilot's tool calls!\n", + "language": "markdown", + "type": "file" + }, + { + "name": "endpoint.py", + "content": "\"\"\"\nAG-UI FastAPI server for SuperOptiX.\n\"\"\"\nimport asyncio\nimport uuid\nfrom typing import List, Optional, Dict, Any\nfrom pathlib import Path\n\nfrom fastapi import FastAPI, Request\nfrom fastapi.responses import StreamingResponse\n\nfrom ag_ui.core import (\n RunAgentInput,\n EventType,\n RunStartedEvent,\n RunFinishedEvent,\n RunErrorEvent,\n Message,\n Tool\n)\nfrom ag_ui.core.events import (\n TextMessageChunkEvent,\n ToolCallChunkEvent,\n StepStartedEvent,\n StepFinishedEvent,\n MessagesSnapshotEvent,\n StateSnapshotEvent,\n CustomEvent,\n)\nfrom ag_ui.encoder import EventEncoder\n\nfrom superoptix.runners.dspy_runner import DSPyRunner\n\nQUEUES = {}\nQUEUES_LOCK = asyncio.Lock()\n\n\nasync def create_queue(runner: DSPyRunner) -> asyncio.Queue:\n \"\"\"Create a queue for a runner.\"\"\"\n queue_id = id(runner)\n async with QUEUES_LOCK:\n queue = asyncio.Queue()\n QUEUES[queue_id] = queue\n return queue\n\n\ndef get_queue(runner: DSPyRunner) -> Optional[asyncio.Queue]:\n \"\"\"Get the queue for a runner.\"\"\"\n queue_id = id(runner)\n return QUEUES.get(queue_id)\n\n\nasync def delete_queue(runner: DSPyRunner) -> None:\n \"\"\"Delete the queue for a runner.\"\"\"\n queue_id = id(runner)\n async with QUEUES_LOCK:\n if queue_id in QUEUES:\n del QUEUES[queue_id]\n\n\ndef superoptix_prepare_inputs(\n *,\n state: dict,\n messages: List[Message],\n tools: List[Tool],\n) -> Dict[str, Any]:\n \"\"\"Prepare inputs for SuperOptiX agent.\"\"\"\n # Convert AG-UI messages to SuperOptiX format\n messages_data = [message.model_dump() for message in messages]\n \n # Remove system message if present\n if len(messages_data) > 0:\n if \"role\" in messages_data[0] and messages_data[0][\"role\"] == \"system\":\n messages_data = messages[1:]\n \n # Convert tools to SuperOptiX format\n tools_data = [tool.model_dump() for tool in tools]\n \n # Prepare state for SuperOptiX\n new_state = {\n **state,\n \"messages\": messages_data,\n \"tools\": tools_data\n }\n \n return new_state\n\n\ndef add_superoptix_fastapi_endpoint(\n app: FastAPI, \n agent_name: str, \n project_root: Optional[Path] = None,\n path: str = \"/\"\n):\n \"\"\"Adds a SuperOptiX endpoint to the FastAPI app.\"\"\"\n \n @app.post(path)\n async def agentic_chat_endpoint(input_data: RunAgentInput, request: Request):\n \"\"\"SuperOptiX agentic chat endpoint\"\"\"\n \n # Initialize SuperOptiX runner\n runner = DSPyRunner(\n agent_name=agent_name,\n project_root=project_root\n )\n \n # Get the accept header from the request\n accept_header = request.headers.get(\"accept\")\n \n # Create an event encoder to properly format SSE events\n encoder = EventEncoder(accept=accept_header)\n \n # Prepare inputs for SuperOptiX\n inputs = superoptix_prepare_inputs(\n state=input_data.state,\n messages=input_data.messages,\n tools=input_data.tools,\n )\n \n # Extract the main query from messages\n query = \"\"\n if input_data.messages:\n # Get the last user message as the query\n for message in reversed(input_data.messages):\n if message.role == \"user\":\n query = message.content or \"\"\n break\n \n async def event_generator():\n try:\n # Send run started event\n yield encoder.encode(\n RunStartedEvent(\n type=EventType.RUN_STARTED,\n thread_id=input_data.thread_id,\n run_id=input_data.run_id\n )\n )\n \n # Send step started event\n step_id = str(uuid.uuid4())\n yield encoder.encode(\n StepStartedEvent(\n type=EventType.STEP_STARTED,\n step_id=step_id,\n name=\"superoptix_agent_execution\"\n )\n )\n \n # Run the SuperOptiX agent\n result = await runner.run(query=query, **inputs)\n \n # Send step finished event\n yield encoder.encode(\n StepFinishedEvent(\n type=EventType.STEP_FINISHED,\n step_id=step_id\n )\n )\n \n # Send the result as a text message\n message_id = str(uuid.uuid4())\n \n # Start message\n yield encoder.encode({\n \"type\": EventType.TEXT_MESSAGE_START,\n \"message_id\": message_id,\n \"role\": \"assistant\"\n })\n \n # Send content in chunks\n if isinstance(result, dict):\n content = result.get(\"implementation\", str(result))\n else:\n content = str(result)\n \n # Split content into chunks for streaming\n chunk_size = 50\n for i in range(0, len(content), chunk_size):\n chunk = content[i:i + chunk_size]\n yield encoder.encode({\n \"type\": EventType.TEXT_MESSAGE_CHUNK,\n \"message_id\": message_id,\n \"delta\": chunk\n })\n \n # End message\n yield encoder.encode({\n \"type\": EventType.TEXT_MESSAGE_END,\n \"message_id\": message_id\n })\n \n # Send run finished event\n yield encoder.encode(\n RunFinishedEvent(\n type=EventType.RUN_FINISHED,\n thread_id=input_data.thread_id,\n run_id=input_data.run_id\n )\n )\n \n except Exception as error:\n yield encoder.encode(\n RunErrorEvent(\n type=EventType.RUN_ERROR,\n thread_id=input_data.thread_id,\n run_id=input_data.run_id,\n error=str(error)\n )\n )\n \n return StreamingResponse(\n event_generator(),\n media_type=encoder.get_content_type()\n ) ", + "language": "python", + "type": "file" + } + ], + "superoptix::agentic_generative_ui": [ + { + "name": "page.tsx", + "content": "\"use client\";\nimport React from \"react\";\nimport \"@copilotkit/react-ui/styles.css\";\nimport \"./style.css\";\nimport { CopilotKit, useCoAgentStateRender } from \"@copilotkit/react-core\";\nimport { CopilotChat } from \"@copilotkit/react-ui\";\nimport { useTheme } from \"next-themes\";\n\ninterface AgenticGenerativeUIProps {\n params: Promise<{\n integrationId: string;\n }>;\n}\n\nconst AgenticGenerativeUI: React.FC = ({ params }) => {\n const { integrationId } = React.use(params);\n return (\n \n \n \n );\n};\n\ninterface AgentState {\n steps: {\n description: string;\n status: \"pending\" | \"completed\";\n }[];\n}\n\nconst Chat = () => {\n const { theme } = useTheme();\n useCoAgentStateRender({\n name: \"agentic_generative_ui\",\n render: ({ state }) => {\n if (!state.steps || state.steps.length === 0) {\n return null;\n }\n\n const completedCount = state.steps.filter(step => step.status === \"completed\").length;\n const progressPercentage = (completedCount / state.steps.length) * 100;\n\n return (\n
\n
\n {/* Header */}\n
\n
\n

\n Task Progress\n

\n
\n {completedCount}/{state.steps.length} Complete\n
\n
\n \n {/* Progress Bar */}\n
\n
\n
\n
\n
\n\n {/* Steps */}\n
\n {state.steps.map((step, index) => {\n const isCompleted = step.status === \"completed\";\n const isCurrentPending = step.status === \"pending\" && \n index === state.steps.findIndex((s) => s.status === \"pending\");\n const isFuturePending = step.status === \"pending\" && !isCurrentPending;\n\n return (\n
\n {/* Connector Line */}\n {index < state.steps.length - 1 && (\n
\n )}\n\n {/* Status Icon */}\n
\n {isCompleted ? (\n \n ) : isCurrentPending ? (\n \n ) : (\n \n )}\n
\n\n {/* Step Content */}\n
\n
\n {step.description}\n
\n {isCurrentPending && (\n
\n Processing...\n
\n )}\n
\n\n {/* Animated Background for Current Step */}\n {isCurrentPending && (\n
\n )}\n
\n );\n })}\n
\n\n {/* Decorative Elements */}\n
\n
\n
\n
\n );\n },\n });\n\n return (\n
\n
\n \n
\n
\n );\n};\n\n// Enhanced Icons\nfunction CheckIcon() {\n return (\n \n \n \n );\n}\n\nfunction SpinnerIcon() {\n return (\n \n \n \n \n );\n}\n\nfunction ClockIcon({ theme }: { theme?: string }) {\n return (\n \n \n \n \n );\n}\n\nexport default AgenticGenerativeUI;\n", + "language": "typescript", + "type": "file" + }, + { + "name": "style.css", + "content": ".copilotKitInput {\n border-bottom-left-radius: 0.75rem;\n border-bottom-right-radius: 0.75rem;\n border-top-left-radius: 0.75rem;\n border-top-right-radius: 0.75rem;\n border: 1px solid var(--copilot-kit-separator-color) !important;\n}\n\n.copilotKitChat {\n background-color: #fff !important;\n}\n", + "language": "css", + "type": "file" + }, + { + "name": "README.mdx", + "content": "# 🚀 Agentic Generative UI Task Executor\n\n## What This Demo Shows\n\nThis demo showcases CopilotKit's **agentic generative UI** capabilities:\n\n1. **Real-time Status Updates**: The Copilot provides live feedback as it works\n through complex tasks\n2. **Long-running Task Execution**: See how agents can handle extended processes\n with continuous feedback\n3. **Dynamic UI Generation**: The interface updates in real-time to reflect the\n agent's progress\n\n## How to Interact\n\nSimply ask your Copilot to perform any moderately complex task:\n\n- \"Make me a sandwich\"\n- \"Plan a vacation to Japan\"\n- \"Create a weekly workout routine\"\n\nThe Copilot will break down the task into steps and begin \"executing\" them,\nproviding real-time status updates as it progresses.\n\n## ✨ Agentic Generative UI in Action\n\n**What's happening technically:**\n\n- The agent analyzes your request and creates a detailed execution plan\n- Each step is processed sequentially with realistic timing\n- Status updates are streamed to the frontend using CopilotKit's streaming\n capabilities\n- The UI dynamically renders these updates without page refreshes\n- The entire flow is managed by the agent, requiring no manual intervention\n\n**What you'll see in this demo:**\n\n- The Copilot breaks your task into logical steps\n- A status indicator shows the current progress\n- Each step is highlighted as it's being executed\n- Detailed status messages explain what's happening at each moment\n- Upon completion, you receive a summary of the task execution\n\nThis pattern of providing real-time progress for long-running tasks is perfect\nfor scenarios where users benefit from transparency into complex processes -\nfrom data analysis to content creation, system configurations, or multi-stage\nworkflows!\n", + "language": "markdown", + "type": "file" + }, + { + "name": "endpoint.py", + "content": "\"\"\"\nAG-UI FastAPI server for SuperOptiX.\n\"\"\"\nimport asyncio\nimport uuid\nfrom typing import List, Optional, Dict, Any\nfrom pathlib import Path\n\nfrom fastapi import FastAPI, Request\nfrom fastapi.responses import StreamingResponse\n\nfrom ag_ui.core import (\n RunAgentInput,\n EventType,\n RunStartedEvent,\n RunFinishedEvent,\n RunErrorEvent,\n Message,\n Tool\n)\nfrom ag_ui.core.events import (\n TextMessageChunkEvent,\n ToolCallChunkEvent,\n StepStartedEvent,\n StepFinishedEvent,\n MessagesSnapshotEvent,\n StateSnapshotEvent,\n CustomEvent,\n)\nfrom ag_ui.encoder import EventEncoder\n\nfrom superoptix.runners.dspy_runner import DSPyRunner\n\nQUEUES = {}\nQUEUES_LOCK = asyncio.Lock()\n\n\nasync def create_queue(runner: DSPyRunner) -> asyncio.Queue:\n \"\"\"Create a queue for a runner.\"\"\"\n queue_id = id(runner)\n async with QUEUES_LOCK:\n queue = asyncio.Queue()\n QUEUES[queue_id] = queue\n return queue\n\n\ndef get_queue(runner: DSPyRunner) -> Optional[asyncio.Queue]:\n \"\"\"Get the queue for a runner.\"\"\"\n queue_id = id(runner)\n return QUEUES.get(queue_id)\n\n\nasync def delete_queue(runner: DSPyRunner) -> None:\n \"\"\"Delete the queue for a runner.\"\"\"\n queue_id = id(runner)\n async with QUEUES_LOCK:\n if queue_id in QUEUES:\n del QUEUES[queue_id]\n\n\ndef superoptix_prepare_inputs(\n *,\n state: dict,\n messages: List[Message],\n tools: List[Tool],\n) -> Dict[str, Any]:\n \"\"\"Prepare inputs for SuperOptiX agent.\"\"\"\n # Convert AG-UI messages to SuperOptiX format\n messages_data = [message.model_dump() for message in messages]\n \n # Remove system message if present\n if len(messages_data) > 0:\n if \"role\" in messages_data[0] and messages_data[0][\"role\"] == \"system\":\n messages_data = messages[1:]\n \n # Convert tools to SuperOptiX format\n tools_data = [tool.model_dump() for tool in tools]\n \n # Prepare state for SuperOptiX\n new_state = {\n **state,\n \"messages\": messages_data,\n \"tools\": tools_data\n }\n \n return new_state\n\n\ndef add_superoptix_fastapi_endpoint(\n app: FastAPI, \n agent_name: str, \n project_root: Optional[Path] = None,\n path: str = \"/\"\n):\n \"\"\"Adds a SuperOptiX endpoint to the FastAPI app.\"\"\"\n \n @app.post(path)\n async def agentic_chat_endpoint(input_data: RunAgentInput, request: Request):\n \"\"\"SuperOptiX agentic chat endpoint\"\"\"\n \n # Initialize SuperOptiX runner\n runner = DSPyRunner(\n agent_name=agent_name,\n project_root=project_root\n )\n \n # Get the accept header from the request\n accept_header = request.headers.get(\"accept\")\n \n # Create an event encoder to properly format SSE events\n encoder = EventEncoder(accept=accept_header)\n \n # Prepare inputs for SuperOptiX\n inputs = superoptix_prepare_inputs(\n state=input_data.state,\n messages=input_data.messages,\n tools=input_data.tools,\n )\n \n # Extract the main query from messages\n query = \"\"\n if input_data.messages:\n # Get the last user message as the query\n for message in reversed(input_data.messages):\n if message.role == \"user\":\n query = message.content or \"\"\n break\n \n async def event_generator():\n try:\n # Send run started event\n yield encoder.encode(\n RunStartedEvent(\n type=EventType.RUN_STARTED,\n thread_id=input_data.thread_id,\n run_id=input_data.run_id\n )\n )\n \n # Send step started event\n step_id = str(uuid.uuid4())\n yield encoder.encode(\n StepStartedEvent(\n type=EventType.STEP_STARTED,\n step_id=step_id,\n name=\"superoptix_agent_execution\"\n )\n )\n \n # Run the SuperOptiX agent\n result = await runner.run(query=query, **inputs)\n \n # Send step finished event\n yield encoder.encode(\n StepFinishedEvent(\n type=EventType.STEP_FINISHED,\n step_id=step_id\n )\n )\n \n # Send the result as a text message\n message_id = str(uuid.uuid4())\n \n # Start message\n yield encoder.encode({\n \"type\": EventType.TEXT_MESSAGE_START,\n \"message_id\": message_id,\n \"role\": \"assistant\"\n })\n \n # Send content in chunks\n if isinstance(result, dict):\n content = result.get(\"implementation\", str(result))\n else:\n content = str(result)\n \n # Split content into chunks for streaming\n chunk_size = 50\n for i in range(0, len(content), chunk_size):\n chunk = content[i:i + chunk_size]\n yield encoder.encode({\n \"type\": EventType.TEXT_MESSAGE_CHUNK,\n \"message_id\": message_id,\n \"delta\": chunk\n })\n \n # End message\n yield encoder.encode({\n \"type\": EventType.TEXT_MESSAGE_END,\n \"message_id\": message_id\n })\n \n # Send run finished event\n yield encoder.encode(\n RunFinishedEvent(\n type=EventType.RUN_FINISHED,\n thread_id=input_data.thread_id,\n run_id=input_data.run_id\n )\n )\n \n except Exception as error:\n yield encoder.encode(\n RunErrorEvent(\n type=EventType.RUN_ERROR,\n thread_id=input_data.thread_id,\n run_id=input_data.run_id,\n error=str(error)\n )\n )\n \n return StreamingResponse(\n event_generator(),\n media_type=encoder.get_content_type()\n ) ", + "language": "python", + "type": "file" + } + ], + "superoptix::shared_state": [ + { + "name": "page.tsx", + "content": "\"use client\";\nimport { CopilotKit, useCoAgent, useCopilotChat } from \"@copilotkit/react-core\";\nimport { CopilotChat, CopilotSidebar } from \"@copilotkit/react-ui\";\nimport React, { useState, useEffect, useRef } from \"react\";\nimport { Role, TextMessage } from \"@copilotkit/runtime-client-gql\";\nimport \"@copilotkit/react-ui/styles.css\";\nimport \"./style.css\";\nimport { useMobileView } from \"@/utils/use-mobile-view\";\nimport { useMobileChat } from \"@/utils/use-mobile-chat\";\n\ninterface SharedStateProps {\n params: Promise<{\n integrationId: string;\n }>;\n}\n\nexport default function SharedState({ params }: SharedStateProps) {\n const { integrationId } = React.use(params);\n const { isMobile } = useMobileView();\n const defaultChatHeight = 50\n const {\n isChatOpen,\n setChatHeight,\n setIsChatOpen,\n isDragging,\n chatHeight,\n handleDragStart\n } = useMobileChat(defaultChatHeight)\n\n const chatTitle = 'AI Recipe Assistant'\n const chatDescription = 'Ask me to craft recipes'\n const initialLabel = 'Hi 👋 How can I help with your recipe?'\n\n return (\n \n \n \n {isMobile ? (\n <>\n {/* Chat Toggle Button */}\n
\n
\n {\n if (!isChatOpen) {\n setChatHeight(defaultChatHeight); // Reset to good default when opening\n }\n setIsChatOpen(!isChatOpen);\n }}\n >\n
\n
\n
{chatTitle}
\n
{chatDescription}
\n
\n
\n
\n \n \n \n
\n
\n
\n\n {/* Pull-Up Chat Container */}\n \n {/* Drag Handle Bar */}\n \n
\n
\n\n {/* Chat Header */}\n
\n
\n
\n

{chatTitle}

\n
\n setIsChatOpen(false)}\n className=\"p-2 hover:bg-gray-100 rounded-full transition-colors\"\n >\n \n \n \n \n
\n
\n\n {/* Chat Content - Flexible container for messages and input */}\n
\n \n
\n
\n\n {/* Backdrop */}\n {isChatOpen && (\n setIsChatOpen(false)}\n />\n )}\n \n ) : (\n \n )}\n
\n \n );\n}\n\nenum SkillLevel {\n BEGINNER = \"Beginner\",\n INTERMEDIATE = \"Intermediate\",\n ADVANCED = \"Advanced\",\n}\n\nenum CookingTime {\n FiveMin = \"5 min\",\n FifteenMin = \"15 min\",\n ThirtyMin = \"30 min\",\n FortyFiveMin = \"45 min\",\n SixtyPlusMin = \"60+ min\",\n}\n\nconst cookingTimeValues = [\n { label: CookingTime.FiveMin, value: 0 },\n { label: CookingTime.FifteenMin, value: 1 },\n { label: CookingTime.ThirtyMin, value: 2 },\n { label: CookingTime.FortyFiveMin, value: 3 },\n { label: CookingTime.SixtyPlusMin, value: 4 },\n];\n\nenum SpecialPreferences {\n HighProtein = \"High Protein\",\n LowCarb = \"Low Carb\",\n Spicy = \"Spicy\",\n BudgetFriendly = \"Budget-Friendly\",\n OnePotMeal = \"One-Pot Meal\",\n Vegetarian = \"Vegetarian\",\n Vegan = \"Vegan\",\n}\n\ninterface Ingredient {\n icon: string;\n name: string;\n amount: string;\n}\n\ninterface Recipe {\n title: string;\n skill_level: SkillLevel;\n cooking_time: CookingTime;\n special_preferences: string[];\n ingredients: Ingredient[];\n instructions: string[];\n}\n\ninterface RecipeAgentState {\n recipe: Recipe;\n}\n\nconst INITIAL_STATE: RecipeAgentState = {\n recipe: {\n title: \"Make Your Recipe\",\n skill_level: SkillLevel.INTERMEDIATE,\n cooking_time: CookingTime.FortyFiveMin,\n special_preferences: [],\n ingredients: [\n { icon: \"🥕\", name: \"Carrots\", amount: \"3 large, grated\" },\n { icon: \"🌾\", name: \"All-Purpose Flour\", amount: \"2 cups\" },\n ],\n instructions: [\"Preheat oven to 350°F (175°C)\"],\n },\n};\n\nfunction Recipe() {\n const { state: agentState, setState: setAgentState } = useCoAgent({\n name: \"shared_state\",\n initialState: INITIAL_STATE,\n });\n\n const [recipe, setRecipe] = useState(INITIAL_STATE.recipe);\n const { appendMessage, isLoading } = useCopilotChat();\n const [editingInstructionIndex, setEditingInstructionIndex] = useState(null);\n const newInstructionRef = useRef(null);\n\n const updateRecipe = (partialRecipe: Partial) => {\n setAgentState({\n ...agentState,\n recipe: {\n ...recipe,\n ...partialRecipe,\n },\n });\n setRecipe({\n ...recipe,\n ...partialRecipe,\n });\n };\n\n const newRecipeState = { ...recipe };\n const newChangedKeys = [];\n const changedKeysRef = useRef([]);\n\n for (const key in recipe) {\n if (\n agentState &&\n agentState.recipe &&\n (agentState.recipe as any)[key] !== undefined &&\n (agentState.recipe as any)[key] !== null\n ) {\n let agentValue = (agentState.recipe as any)[key];\n const recipeValue = (recipe as any)[key];\n\n // Check if agentValue is a string and replace \\n with actual newlines\n if (typeof agentValue === \"string\") {\n agentValue = agentValue.replace(/\\\\n/g, \"\\n\");\n }\n\n if (JSON.stringify(agentValue) !== JSON.stringify(recipeValue)) {\n (newRecipeState as any)[key] = agentValue;\n newChangedKeys.push(key);\n }\n }\n }\n\n if (newChangedKeys.length > 0) {\n changedKeysRef.current = newChangedKeys;\n } else if (!isLoading) {\n changedKeysRef.current = [];\n }\n\n useEffect(() => {\n setRecipe(newRecipeState);\n }, [JSON.stringify(newRecipeState)]);\n\n const handleTitleChange = (event: React.ChangeEvent) => {\n updateRecipe({\n title: event.target.value,\n });\n };\n\n const handleSkillLevelChange = (event: React.ChangeEvent) => {\n updateRecipe({\n skill_level: event.target.value as SkillLevel,\n });\n };\n\n const handleDietaryChange = (preference: string, checked: boolean) => {\n if (checked) {\n updateRecipe({\n special_preferences: [...recipe.special_preferences, preference],\n });\n } else {\n updateRecipe({\n special_preferences: recipe.special_preferences.filter((p) => p !== preference),\n });\n }\n };\n\n const handleCookingTimeChange = (event: React.ChangeEvent) => {\n updateRecipe({\n cooking_time: cookingTimeValues[Number(event.target.value)].label,\n });\n };\n\n const addIngredient = () => {\n // Pick a random food emoji from our valid list\n updateRecipe({\n ingredients: [...recipe.ingredients, { icon: \"🍴\", name: \"\", amount: \"\" }],\n });\n };\n\n const updateIngredient = (index: number, field: keyof Ingredient, value: string) => {\n const updatedIngredients = [...recipe.ingredients];\n updatedIngredients[index] = {\n ...updatedIngredients[index],\n [field]: value,\n };\n updateRecipe({ ingredients: updatedIngredients });\n };\n\n const removeIngredient = (index: number) => {\n const updatedIngredients = [...recipe.ingredients];\n updatedIngredients.splice(index, 1);\n updateRecipe({ ingredients: updatedIngredients });\n };\n\n const addInstruction = () => {\n const newIndex = recipe.instructions.length;\n updateRecipe({\n instructions: [...recipe.instructions, \"\"],\n });\n // Set the new instruction as the editing one\n setEditingInstructionIndex(newIndex);\n\n // Focus the new instruction after render\n setTimeout(() => {\n const textareas = document.querySelectorAll(\".instructions-container textarea\");\n const newTextarea = textareas[textareas.length - 1] as HTMLTextAreaElement;\n if (newTextarea) {\n newTextarea.focus();\n }\n }, 50);\n };\n\n const updateInstruction = (index: number, value: string) => {\n const updatedInstructions = [...recipe.instructions];\n updatedInstructions[index] = value;\n updateRecipe({ instructions: updatedInstructions });\n };\n\n const removeInstruction = (index: number) => {\n const updatedInstructions = [...recipe.instructions];\n updatedInstructions.splice(index, 1);\n updateRecipe({ instructions: updatedInstructions });\n };\n\n // Simplified icon handler that defaults to a fork/knife for any problematic icons\n const getProperIcon = (icon: string | undefined): string => {\n // If icon is undefined return the default\n if (!icon) {\n return \"🍴\";\n }\n\n return icon;\n };\n\n return (\n
\n {/* Recipe Title */}\n
\n \n\n
\n
\n 🕒\n t.label === recipe.cooking_time)?.value || 3}\n onChange={handleCookingTimeChange}\n style={{\n backgroundImage:\n \"url(\\\"data:image/svg+xml;charset=UTF-8,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23555' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3e%3cpolyline points='6 9 12 15 18 9'%3e%3c/polyline%3e%3c/svg%3e\\\")\",\n backgroundRepeat: \"no-repeat\",\n backgroundPosition: \"right 0px center\",\n backgroundSize: \"12px\",\n appearance: \"none\",\n WebkitAppearance: \"none\",\n }}\n >\n {cookingTimeValues.map((time) => (\n \n ))}\n \n
\n\n
\n 🏆\n \n {Object.values(SkillLevel).map((level) => (\n \n ))}\n \n
\n
\n
\n\n {/* Dietary Preferences */}\n
\n {changedKeysRef.current.includes(\"special_preferences\") && }\n

Dietary Preferences

\n
\n {Object.values(SpecialPreferences).map((option) => (\n \n ))}\n
\n
\n\n {/* Ingredients */}\n
\n {changedKeysRef.current.includes(\"ingredients\") && }\n
\n

Ingredients

\n \n
\n
\n {recipe.ingredients.map((ingredient, index) => (\n
\n
{getProperIcon(ingredient.icon)}
\n
\n updateIngredient(index, \"name\", e.target.value)}\n placeholder=\"Ingredient name\"\n className=\"ingredient-name-input\"\n />\n updateIngredient(index, \"amount\", e.target.value)}\n placeholder=\"Amount\"\n className=\"ingredient-amount-input\"\n />\n
\n removeIngredient(index)}\n aria-label=\"Remove ingredient\"\n >\n ×\n \n
\n ))}\n
\n
\n\n {/* Instructions */}\n
\n {changedKeysRef.current.includes(\"instructions\") && }\n
\n

Instructions

\n \n
\n
\n {recipe.instructions.map((instruction, index) => (\n
\n {/* Number Circle */}\n
{index + 1}
\n\n {/* Vertical Line */}\n {index < recipe.instructions.length - 1 &&
}\n\n {/* Instruction Content */}\n setEditingInstructionIndex(index)}\n >\n updateInstruction(index, e.target.value)}\n placeholder={!instruction ? \"Enter cooking instruction...\" : \"\"}\n onFocus={() => setEditingInstructionIndex(index)}\n onBlur={(e) => {\n // Only blur if clicking outside this instruction\n if (!e.relatedTarget || !e.currentTarget.contains(e.relatedTarget as Node)) {\n setEditingInstructionIndex(null);\n }\n }}\n />\n\n {/* Delete Button (only visible on hover) */}\n {\n e.stopPropagation(); // Prevent triggering parent onClick\n removeInstruction(index);\n }}\n aria-label=\"Remove instruction\"\n >\n ×\n \n
\n
\n ))}\n
\n
\n\n {/* Improve with AI Button */}\n
\n {\n if (!isLoading) {\n appendMessage(\n new TextMessage({\n content: \"Improve the recipe\",\n role: Role.User,\n }),\n );\n }\n }}\n disabled={isLoading}\n >\n {isLoading ? \"Please Wait...\" : \"Improve with AI\"}\n \n
\n
\n );\n}\n\nfunction Ping() {\n return (\n \n \n \n \n );\n}\n", + "language": "typescript", + "type": "file" + }, + { + "name": "style.css", + "content": ".copilotKitWindow {\n box-shadow: 0 2px 10px rgba(0, 0, 0, 0.1);\n}\n\n.copilotKitHeader {\n border-top-left-radius: 5px !important;\n background-color: #fff;\n color: #000;\n border-bottom: 0px;\n}\n\n/* Recipe App Styles */\n.app-container {\n min-height: 100vh;\n width: 100%;\n display: flex;\n align-items: center;\n justify-content: center;\n background-size: cover;\n background-position: center;\n background-repeat: no-repeat;\n background-attachment: fixed;\n position: relative;\n overflow: auto;\n}\n\n.recipe-card {\n background-color: rgba(255, 255, 255, 0.97);\n border-radius: 16px;\n box-shadow: 0 15px 30px rgba(0, 0, 0, 0.25), 0 5px 15px rgba(0, 0, 0, 0.15);\n width: 100%;\n max-width: 750px;\n margin: 20px auto;\n padding: 14px 32px;\n position: relative;\n z-index: 1;\n backdrop-filter: blur(5px);\n border: 1px solid rgba(255, 255, 255, 0.3);\n transition: transform 0.2s ease, box-shadow 0.2s ease;\n animation: fadeIn 0.5s ease-out forwards;\n box-sizing: border-box;\n overflow: hidden;\n}\n\n.recipe-card:hover {\n transform: translateY(-5px);\n box-shadow: 0 20px 40px rgba(0, 0, 0, 0.3), 0 10px 20px rgba(0, 0, 0, 0.2);\n}\n\n/* Recipe Header */\n.recipe-header {\n margin-bottom: 24px;\n}\n\n.recipe-title-input {\n width: 100%;\n font-size: 24px;\n font-weight: bold;\n border: none;\n outline: none;\n padding: 8px 0;\n margin-bottom: 0px;\n}\n\n.recipe-meta {\n display: flex;\n align-items: center;\n gap: 20px;\n margin-top: 5px;\n margin-bottom: 14px;\n}\n\n.meta-item {\n display: flex;\n align-items: center;\n gap: 8px;\n color: #555;\n}\n\n.meta-icon {\n font-size: 20px;\n color: #777;\n}\n\n.meta-text {\n font-size: 15px;\n}\n\n/* Recipe Meta Selects */\n.meta-item select {\n border: none;\n background: transparent;\n font-size: 15px;\n color: #555;\n cursor: pointer;\n outline: none;\n padding-right: 18px;\n transition: color 0.2s, transform 0.1s;\n font-weight: 500;\n}\n\n.meta-item select:hover,\n.meta-item select:focus {\n color: #FF5722;\n}\n\n.meta-item select:active {\n transform: scale(0.98);\n}\n\n.meta-item select option {\n color: #333;\n background-color: white;\n font-weight: normal;\n padding: 8px;\n}\n\n/* Section Container */\n.section-container {\n margin-bottom: 20px;\n position: relative;\n width: 100%;\n}\n\n.section-title {\n font-size: 20px;\n font-weight: 700;\n margin-bottom: 20px;\n color: #333;\n position: relative;\n display: inline-block;\n}\n\n.section-title:after {\n content: \"\";\n position: absolute;\n bottom: -8px;\n left: 0;\n width: 40px;\n height: 3px;\n background-color: #ff7043;\n border-radius: 3px;\n}\n\n/* Dietary Preferences */\n.dietary-options {\n display: flex;\n flex-wrap: wrap;\n gap: 10px 16px;\n margin-bottom: 16px;\n width: 100%;\n}\n\n.dietary-option {\n display: flex;\n align-items: center;\n gap: 6px;\n font-size: 14px;\n cursor: pointer;\n margin-bottom: 4px;\n}\n\n.dietary-option input {\n cursor: pointer;\n}\n\n/* Ingredients */\n.ingredients-container {\n display: flex;\n flex-wrap: wrap;\n gap: 10px;\n margin-bottom: 15px;\n width: 100%;\n box-sizing: border-box;\n}\n\n.ingredient-card {\n display: flex;\n align-items: center;\n background-color: rgba(255, 255, 255, 0.9);\n border-radius: 12px;\n padding: 12px;\n margin-bottom: 10px;\n box-shadow: 0 4px 10px rgba(0, 0, 0, 0.08);\n position: relative;\n transition: all 0.2s ease;\n border: 1px solid rgba(240, 240, 240, 0.8);\n width: calc(33.333% - 7px);\n box-sizing: border-box;\n}\n\n.ingredient-card:hover {\n transform: translateY(-2px);\n box-shadow: 0 6px 15px rgba(0, 0, 0, 0.12);\n}\n\n.ingredient-card .remove-button {\n position: absolute;\n right: 10px;\n top: 10px;\n background: none;\n border: none;\n color: #ccc;\n font-size: 16px;\n cursor: pointer;\n display: none;\n padding: 0;\n width: 24px;\n height: 24px;\n line-height: 1;\n}\n\n.ingredient-card:hover .remove-button {\n display: block;\n}\n\n.ingredient-icon {\n font-size: 24px;\n margin-right: 12px;\n display: flex;\n align-items: center;\n justify-content: center;\n width: 40px;\n height: 40px;\n background-color: #f7f7f7;\n border-radius: 50%;\n flex-shrink: 0;\n}\n\n.ingredient-content {\n flex: 1;\n display: flex;\n flex-direction: column;\n gap: 3px;\n min-width: 0;\n}\n\n.ingredient-name-input,\n.ingredient-amount-input {\n border: none;\n background: transparent;\n outline: none;\n width: 100%;\n padding: 0;\n text-overflow: ellipsis;\n overflow: hidden;\n white-space: nowrap;\n}\n\n.ingredient-name-input {\n font-weight: 500;\n font-size: 14px;\n}\n\n.ingredient-amount-input {\n font-size: 13px;\n color: #666;\n}\n\n.ingredient-name-input::placeholder,\n.ingredient-amount-input::placeholder {\n color: #aaa;\n}\n\n.remove-button {\n background: none;\n border: none;\n color: #999;\n font-size: 20px;\n cursor: pointer;\n padding: 0;\n width: 28px;\n height: 28px;\n display: flex;\n align-items: center;\n justify-content: center;\n margin-left: 10px;\n}\n\n.remove-button:hover {\n color: #FF5722;\n}\n\n/* Instructions */\n.instructions-container {\n display: flex;\n flex-direction: column;\n gap: 6px;\n position: relative;\n margin-bottom: 12px;\n width: 100%;\n}\n\n.instruction-item {\n position: relative;\n display: flex;\n width: 100%;\n box-sizing: border-box;\n margin-bottom: 8px;\n align-items: flex-start;\n}\n\n.instruction-number {\n display: flex;\n align-items: center;\n justify-content: center;\n min-width: 26px;\n height: 26px;\n background-color: #ff7043;\n color: white;\n border-radius: 50%;\n font-weight: 600;\n flex-shrink: 0;\n box-shadow: 0 2px 4px rgba(255, 112, 67, 0.3);\n z-index: 1;\n font-size: 13px;\n margin-top: 2px;\n}\n\n.instruction-line {\n position: absolute;\n left: 13px; /* Half of the number circle width */\n top: 22px;\n bottom: -18px;\n width: 2px;\n background: linear-gradient(to bottom, #ff7043 60%, rgba(255, 112, 67, 0.4));\n z-index: 0;\n}\n\n.instruction-content {\n background-color: white;\n border-radius: 10px;\n padding: 10px 14px;\n margin-left: 12px;\n flex-grow: 1;\n transition: all 0.2s ease;\n box-shadow: 0 2px 6px rgba(0, 0, 0, 0.08);\n border: 1px solid rgba(240, 240, 240, 0.8);\n position: relative;\n width: calc(100% - 38px);\n box-sizing: border-box;\n display: flex;\n align-items: center;\n}\n\n.instruction-content-editing {\n background-color: #fff9f6;\n box-shadow: 0 6px 16px rgba(0, 0, 0, 0.12), 0 0 0 2px rgba(255, 112, 67, 0.2);\n}\n\n.instruction-content:hover {\n transform: translateY(-2px);\n box-shadow: 0 6px 16px rgba(0, 0, 0, 0.12);\n}\n\n.instruction-textarea {\n width: 100%;\n background: transparent;\n border: none;\n resize: vertical;\n font-family: inherit;\n font-size: 14px;\n line-height: 1.4;\n min-height: 20px;\n outline: none;\n padding: 0;\n margin: 0;\n}\n\n.instruction-delete-btn {\n position: absolute;\n background: none;\n border: none;\n color: #ccc;\n font-size: 16px;\n cursor: pointer;\n display: none;\n padding: 0;\n width: 20px;\n height: 20px;\n line-height: 1;\n top: 50%;\n transform: translateY(-50%);\n right: 8px;\n}\n\n.instruction-content:hover .instruction-delete-btn {\n display: flex;\n align-items: center;\n justify-content: center;\n}\n\n/* Action Button */\n.action-container {\n display: flex;\n justify-content: center;\n margin-top: 40px;\n padding-bottom: 20px;\n position: relative;\n}\n\n.improve-button {\n background-color: #ff7043;\n border: none;\n color: white;\n border-radius: 30px;\n font-size: 18px;\n font-weight: 600;\n padding: 14px 28px;\n cursor: pointer;\n transition: all 0.3s ease;\n box-shadow: 0 4px 15px rgba(255, 112, 67, 0.4);\n display: flex;\n align-items: center;\n justify-content: center;\n text-align: center;\n position: relative;\n min-width: 180px;\n}\n\n.improve-button:hover {\n background-color: #ff5722;\n transform: translateY(-2px);\n box-shadow: 0 8px 20px rgba(255, 112, 67, 0.5);\n}\n\n.improve-button.loading {\n background-color: #ff7043;\n opacity: 0.8;\n cursor: not-allowed;\n padding-left: 42px; /* Reduced padding to bring text closer to icon */\n padding-right: 22px; /* Balance the button */\n justify-content: flex-start; /* Left align text for better alignment with icon */\n}\n\n.improve-button.loading:after {\n content: \"\"; /* Add space between icon and text */\n display: inline-block;\n width: 8px; /* Width of the space */\n}\n\n.improve-button:before {\n content: \"\";\n background-image: url(\"data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='white' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M12 2v4M12 18v4M4.93 4.93l2.83 2.83M16.24 16.24l2.83 2.83M2 12h4M18 12h4M4.93 19.07l2.83-2.83M16.24 7.76l2.83-2.83'/%3E%3C/svg%3E\");\n width: 20px; /* Slightly smaller icon */\n height: 20px;\n background-repeat: no-repeat;\n background-size: contain;\n position: absolute;\n left: 16px; /* Slightly adjusted */\n top: 50%;\n transform: translateY(-50%);\n display: none;\n}\n\n.improve-button.loading:before {\n display: block;\n animation: spin 1.5s linear infinite;\n}\n\n@keyframes spin {\n 0% { transform: translateY(-50%) rotate(0deg); }\n 100% { transform: translateY(-50%) rotate(360deg); }\n}\n\n/* Ping Animation */\n.ping-animation {\n position: absolute;\n display: flex;\n width: 12px;\n height: 12px;\n top: 0;\n right: 0;\n}\n\n.ping-circle {\n position: absolute;\n display: inline-flex;\n width: 100%;\n height: 100%;\n border-radius: 50%;\n background-color: #38BDF8;\n opacity: 0.75;\n animation: ping 1.5s cubic-bezier(0, 0, 0.2, 1) infinite;\n}\n\n.ping-dot {\n position: relative;\n display: inline-flex;\n width: 12px;\n height: 12px;\n border-radius: 50%;\n background-color: #0EA5E9;\n}\n\n@keyframes ping {\n 75%, 100% {\n transform: scale(2);\n opacity: 0;\n }\n}\n\n/* Instruction hover effects */\n.instruction-item:hover .instruction-delete-btn {\n display: flex !important;\n}\n\n/* Add some subtle animations */\n@keyframes fadeIn {\n from { opacity: 0; transform: translateY(20px); }\n to { opacity: 1; transform: translateY(0); }\n}\n\n/* Better center alignment for the recipe card */\n.recipe-card-container {\n display: flex;\n justify-content: center;\n width: 100%;\n position: relative;\n z-index: 1;\n margin: 0 auto;\n box-sizing: border-box;\n}\n\n/* Add Buttons */\n.add-button {\n background-color: transparent;\n color: #FF5722;\n border: 1px dashed #FF5722;\n border-radius: 8px;\n padding: 10px 16px;\n cursor: pointer;\n font-weight: 500;\n display: inline-block;\n font-size: 14px;\n margin-bottom: 0;\n}\n\n.add-step-button {\n background-color: transparent;\n color: #FF5722;\n border: 1px dashed #FF5722;\n border-radius: 6px;\n padding: 6px 12px;\n cursor: pointer;\n font-weight: 500;\n font-size: 13px;\n}\n\n/* Section Headers */\n.section-header {\n display: flex;\n justify-content: space-between;\n align-items: center;\n margin-bottom: 12px;\n}", + "language": "css", + "type": "file" + }, + { + "name": "README.mdx", + "content": "# 🍳 Shared State Recipe Creator\n\n## What This Demo Shows\n\nThis demo showcases CopilotKit's **shared state** functionality - a powerful\nfeature that enables bidirectional data flow between:\n\n1. **Frontend → Agent**: UI controls update the agent's context in real-time\n2. **Agent → Frontend**: The Copilot's recipe creations instantly update the UI\n components\n\nIt's like having a cooking buddy who not only listens to what you want but also\nupdates your recipe card as you chat - no refresh needed! ✨\n\n## How to Interact\n\nMix and match any of these parameters (or none at all - it's up to you!):\n\n- **Skill Level**: Beginner to expert 👨‍🍳\n- **Cooking Time**: Quick meals or slow cooking ⏱️\n- **Special Preferences**: Dietary needs, flavor profiles, health goals 🥗\n- **Ingredients**: Items you want to include 🧅🥩🍄\n- **Instructions**: Any specific steps\n\nThen chat with your Copilot chef with prompts like:\n\n- \"I'm a beginner cook. Can you make me a quick dinner?\"\n- \"I need something spicy with chicken that takes under 30 minutes!\"\n\n## ✨ Shared State Magic in Action\n\n**What's happening technically:**\n\n- The UI and Copilot agent share the same state object (**Agent State = UI\n State**)\n- Changes from either side automatically update the other\n- Neither side needs to manually request updates from the other\n\n**What you'll see in this demo:**\n\n- Set cooking time to 20 minutes in the UI and watch the Copilot immediately\n respect your time constraint\n- Add ingredients through the UI and see them appear in your recipe\n- When the Copilot suggests new ingredients, watch them automatically appear in\n the UI ingredients list\n- Change your skill level and see how the Copilot adapts its instructions in\n real-time\n\nThis synchronized state creates a seamless experience where the agent always has\nyour current preferences, and any updates to the recipe are instantly reflected\nin both places.\n\nThis shared state pattern can be applied to any application where you want your\nUI and Copilot to work together in perfect harmony!\n", + "language": "markdown", + "type": "file" + }, + { + "name": "endpoint.py", + "content": "\"\"\"\nAG-UI FastAPI server for SuperOptiX.\n\"\"\"\nimport asyncio\nimport uuid\nfrom typing import List, Optional, Dict, Any\nfrom pathlib import Path\n\nfrom fastapi import FastAPI, Request\nfrom fastapi.responses import StreamingResponse\n\nfrom ag_ui.core import (\n RunAgentInput,\n EventType,\n RunStartedEvent,\n RunFinishedEvent,\n RunErrorEvent,\n Message,\n Tool\n)\nfrom ag_ui.core.events import (\n TextMessageChunkEvent,\n ToolCallChunkEvent,\n StepStartedEvent,\n StepFinishedEvent,\n MessagesSnapshotEvent,\n StateSnapshotEvent,\n CustomEvent,\n)\nfrom ag_ui.encoder import EventEncoder\n\nfrom superoptix.runners.dspy_runner import DSPyRunner\n\nQUEUES = {}\nQUEUES_LOCK = asyncio.Lock()\n\n\nasync def create_queue(runner: DSPyRunner) -> asyncio.Queue:\n \"\"\"Create a queue for a runner.\"\"\"\n queue_id = id(runner)\n async with QUEUES_LOCK:\n queue = asyncio.Queue()\n QUEUES[queue_id] = queue\n return queue\n\n\ndef get_queue(runner: DSPyRunner) -> Optional[asyncio.Queue]:\n \"\"\"Get the queue for a runner.\"\"\"\n queue_id = id(runner)\n return QUEUES.get(queue_id)\n\n\nasync def delete_queue(runner: DSPyRunner) -> None:\n \"\"\"Delete the queue for a runner.\"\"\"\n queue_id = id(runner)\n async with QUEUES_LOCK:\n if queue_id in QUEUES:\n del QUEUES[queue_id]\n\n\ndef superoptix_prepare_inputs(\n *,\n state: dict,\n messages: List[Message],\n tools: List[Tool],\n) -> Dict[str, Any]:\n \"\"\"Prepare inputs for SuperOptiX agent.\"\"\"\n # Convert AG-UI messages to SuperOptiX format\n messages_data = [message.model_dump() for message in messages]\n \n # Remove system message if present\n if len(messages_data) > 0:\n if \"role\" in messages_data[0] and messages_data[0][\"role\"] == \"system\":\n messages_data = messages[1:]\n \n # Convert tools to SuperOptiX format\n tools_data = [tool.model_dump() for tool in tools]\n \n # Prepare state for SuperOptiX\n new_state = {\n **state,\n \"messages\": messages_data,\n \"tools\": tools_data\n }\n \n return new_state\n\n\ndef add_superoptix_fastapi_endpoint(\n app: FastAPI, \n agent_name: str, \n project_root: Optional[Path] = None,\n path: str = \"/\"\n):\n \"\"\"Adds a SuperOptiX endpoint to the FastAPI app.\"\"\"\n \n @app.post(path)\n async def agentic_chat_endpoint(input_data: RunAgentInput, request: Request):\n \"\"\"SuperOptiX agentic chat endpoint\"\"\"\n \n # Initialize SuperOptiX runner\n runner = DSPyRunner(\n agent_name=agent_name,\n project_root=project_root\n )\n \n # Get the accept header from the request\n accept_header = request.headers.get(\"accept\")\n \n # Create an event encoder to properly format SSE events\n encoder = EventEncoder(accept=accept_header)\n \n # Prepare inputs for SuperOptiX\n inputs = superoptix_prepare_inputs(\n state=input_data.state,\n messages=input_data.messages,\n tools=input_data.tools,\n )\n \n # Extract the main query from messages\n query = \"\"\n if input_data.messages:\n # Get the last user message as the query\n for message in reversed(input_data.messages):\n if message.role == \"user\":\n query = message.content or \"\"\n break\n \n async def event_generator():\n try:\n # Send run started event\n yield encoder.encode(\n RunStartedEvent(\n type=EventType.RUN_STARTED,\n thread_id=input_data.thread_id,\n run_id=input_data.run_id\n )\n )\n \n # Send step started event\n step_id = str(uuid.uuid4())\n yield encoder.encode(\n StepStartedEvent(\n type=EventType.STEP_STARTED,\n step_id=step_id,\n name=\"superoptix_agent_execution\"\n )\n )\n \n # Run the SuperOptiX agent\n result = await runner.run(query=query, **inputs)\n \n # Send step finished event\n yield encoder.encode(\n StepFinishedEvent(\n type=EventType.STEP_FINISHED,\n step_id=step_id\n )\n )\n \n # Send the result as a text message\n message_id = str(uuid.uuid4())\n \n # Start message\n yield encoder.encode({\n \"type\": EventType.TEXT_MESSAGE_START,\n \"message_id\": message_id,\n \"role\": \"assistant\"\n })\n \n # Send content in chunks\n if isinstance(result, dict):\n content = result.get(\"implementation\", str(result))\n else:\n content = str(result)\n \n # Split content into chunks for streaming\n chunk_size = 50\n for i in range(0, len(content), chunk_size):\n chunk = content[i:i + chunk_size]\n yield encoder.encode({\n \"type\": EventType.TEXT_MESSAGE_CHUNK,\n \"message_id\": message_id,\n \"delta\": chunk\n })\n \n # End message\n yield encoder.encode({\n \"type\": EventType.TEXT_MESSAGE_END,\n \"message_id\": message_id\n })\n \n # Send run finished event\n yield encoder.encode(\n RunFinishedEvent(\n type=EventType.RUN_FINISHED,\n thread_id=input_data.thread_id,\n run_id=input_data.run_id\n )\n )\n \n except Exception as error:\n yield encoder.encode(\n RunErrorEvent(\n type=EventType.RUN_ERROR,\n thread_id=input_data.thread_id,\n run_id=input_data.run_id,\n error=str(error)\n )\n )\n \n return StreamingResponse(\n event_generator(),\n media_type=encoder.get_content_type()\n ) ", + "language": "python", + "type": "file" + } + ], + "superoptix::predictive_state_updates": [ + { + "name": "page.tsx", + "content": "\"use client\";\nimport \"@copilotkit/react-ui/styles.css\";\nimport \"./style.css\";\n\nimport MarkdownIt from \"markdown-it\";\nimport React from \"react\";\n\nimport { diffWords } from \"diff\";\nimport { useEditor, EditorContent } from \"@tiptap/react\";\nimport StarterKit from \"@tiptap/starter-kit\";\nimport { useEffect, useState } from \"react\";\nimport { CopilotKit, useCoAgent, useCopilotAction, useCopilotChat } from \"@copilotkit/react-core\";\nimport { CopilotChat, CopilotSidebar } from \"@copilotkit/react-ui\";\nimport { useMobileView } from \"@/utils/use-mobile-view\";\nimport { useMobileChat } from \"@/utils/use-mobile-chat\";\n\nconst extensions = [StarterKit];\n\ninterface PredictiveStateUpdatesProps {\n params: Promise<{\n integrationId: string;\n }>;\n}\n\nexport default function PredictiveStateUpdates({ params }: PredictiveStateUpdatesProps) {\n const { integrationId } = React.use(params);\n const { isMobile } = useMobileView();\n const defaultChatHeight = 50\n const {\n isChatOpen,\n setChatHeight,\n setIsChatOpen,\n isDragging,\n chatHeight,\n handleDragStart\n } = useMobileChat(defaultChatHeight)\n const chatTitle = 'AI Document Editor'\n const chatDescription = 'Ask me to create or edit a document'\n const initialLabel = 'Hi 👋 How can I help with your document?'\n\n return (\n \n \n {isMobile ? (\n <>\n {/* Chat Toggle Button */}\n
\n
\n {\n if (!isChatOpen) {\n setChatHeight(defaultChatHeight); // Reset to good default when opening\n }\n setIsChatOpen(!isChatOpen);\n }}\n >\n
\n
\n
{chatTitle}
\n
{chatDescription}
\n
\n
\n
\n \n \n \n
\n
\n
\n\n {/* Pull-Up Chat Container */}\n \n {/* Drag Handle Bar */}\n \n
\n
\n\n {/* Chat Header */}\n
\n
\n
\n

{chatTitle}

\n
\n setIsChatOpen(false)}\n className=\"p-2 hover:bg-gray-100 rounded-full transition-colors\"\n >\n \n \n \n \n
\n
\n\n {/* Chat Content - Flexible container for messages and input */}\n
\n \n
\n \n\n {/* Backdrop */}\n {isChatOpen && (\n setIsChatOpen(false)}\n />\n )}\n \n ) : (\n \n )}\n \n \n \n );\n}\n\ninterface AgentState {\n document: string;\n}\n\nconst DocumentEditor = () => {\n const editor = useEditor({\n extensions,\n immediatelyRender: false,\n editorProps: {\n attributes: { class: \"min-h-screen p-10\" },\n },\n });\n const [placeholderVisible, setPlaceholderVisible] = useState(false);\n const [currentDocument, setCurrentDocument] = useState(\"\");\n const { isLoading } = useCopilotChat();\n\n const {\n state: agentState,\n setState: setAgentState,\n nodeName,\n } = useCoAgent({\n name: \"predictive_state_updates\",\n initialState: {\n document: \"\",\n },\n });\n\n useEffect(() => {\n if (isLoading) {\n setCurrentDocument(editor?.getText() || \"\");\n }\n editor?.setEditable(!isLoading);\n }, [isLoading]);\n\n useEffect(() => {\n if (nodeName == \"end\") {\n // set the text one final time when loading is done\n if (currentDocument.trim().length > 0 && currentDocument !== agentState?.document) {\n const newDocument = agentState?.document || \"\";\n const diff = diffPartialText(currentDocument, newDocument, true);\n const markdown = fromMarkdown(diff);\n editor?.commands.setContent(markdown);\n }\n }\n }, [nodeName]);\n\n useEffect(() => {\n if (isLoading) {\n if (currentDocument.trim().length > 0) {\n const newDocument = agentState?.document || \"\";\n const diff = diffPartialText(currentDocument, newDocument);\n const markdown = fromMarkdown(diff);\n editor?.commands.setContent(markdown);\n } else {\n const markdown = fromMarkdown(agentState?.document || \"\");\n editor?.commands.setContent(markdown);\n }\n }\n }, [agentState?.document]);\n\n const text = editor?.getText() || \"\";\n\n useEffect(() => {\n setPlaceholderVisible(text.length === 0);\n\n if (!isLoading) {\n setCurrentDocument(text);\n setAgentState({\n document: text,\n });\n }\n }, [text]);\n\n // TODO(steve): Remove this when all agents have been updated to use write_document tool.\n useCopilotAction({\n name: \"confirm_changes\",\n renderAndWaitForResponse: ({ args, respond, status }) => (\n {\n editor?.commands.setContent(fromMarkdown(currentDocument));\n setAgentState({ document: currentDocument });\n }}\n onConfirm={() => {\n editor?.commands.setContent(fromMarkdown(agentState?.document || \"\"));\n setCurrentDocument(agentState?.document || \"\");\n setAgentState({ document: agentState?.document || \"\" });\n }}\n />\n ),\n });\n\n // Action to write the document.\n useCopilotAction({\n name: \"write_document\",\n description: `Present the proposed changes to the user for review`,\n parameters: [\n {\n name: \"document\",\n type: \"string\",\n description: \"The full updated document in markdown format\",\n },\n ],\n renderAndWaitForResponse({ args, status, respond }) {\n if (status === \"executing\") {\n return (\n {\n editor?.commands.setContent(fromMarkdown(currentDocument));\n setAgentState({ document: currentDocument });\n }}\n onConfirm={() => {\n editor?.commands.setContent(fromMarkdown(agentState?.document || \"\"));\n setCurrentDocument(agentState?.document || \"\");\n setAgentState({ document: agentState?.document || \"\" });\n }}\n />\n );\n }\n return <>;\n },\n });\n\n return (\n
\n {placeholderVisible && (\n
\n Write whatever you want here in Markdown format...\n
\n )}\n \n
\n );\n};\n\ninterface ConfirmChangesProps {\n args: any;\n respond: any;\n status: any;\n onReject: () => void;\n onConfirm: () => void;\n}\n\nfunction ConfirmChanges({ args, respond, status, onReject, onConfirm }: ConfirmChangesProps) {\n const [accepted, setAccepted] = useState(null);\n return (\n
\n

Confirm Changes

\n

Do you want to accept the changes?

\n {accepted === null && (\n
\n {\n if (respond) {\n setAccepted(false);\n onReject();\n respond({ accepted: false });\n }\n }}\n >\n Reject\n \n {\n if (respond) {\n setAccepted(true);\n onConfirm();\n respond({ accepted: true });\n }\n }}\n >\n Confirm\n \n
\n )}\n {accepted !== null && (\n
\n
\n {accepted ? \"✓ Accepted\" : \"✗ Rejected\"}\n
\n
\n )}\n
\n );\n}\n\nfunction fromMarkdown(text: string) {\n const md = new MarkdownIt({\n typographer: true,\n html: true,\n });\n\n return md.render(text);\n}\n\nfunction diffPartialText(oldText: string, newText: string, isComplete: boolean = false) {\n let oldTextToCompare = oldText;\n if (oldText.length > newText.length && !isComplete) {\n // make oldText shorter\n oldTextToCompare = oldText.slice(0, newText.length);\n }\n\n const changes = diffWords(oldTextToCompare, newText);\n\n let result = \"\";\n changes.forEach((part) => {\n if (part.added) {\n result += `${part.value}`;\n } else if (part.removed) {\n result += `${part.value}`;\n } else {\n result += part.value;\n }\n });\n\n if (oldText.length > newText.length && !isComplete) {\n result += oldText.slice(newText.length);\n }\n\n return result;\n}\n\nfunction isAlpha(text: string) {\n return /[a-zA-Z\\u00C0-\\u017F]/.test(text.trim());\n}\n", + "language": "typescript", + "type": "file" + }, + { + "name": "style.css", + "content": "/* Basic editor styles */\n.tiptap-container {\n height: 100vh; /* Full viewport height */\n width: 100vw; /* Full viewport width */\n display: flex;\n flex-direction: column;\n}\n\n.tiptap {\n flex: 1; /* Take up remaining space */\n overflow: auto; /* Allow scrolling if content overflows */\n}\n\n.tiptap :first-child {\n margin-top: 0;\n}\n\n/* List styles */\n.tiptap ul,\n.tiptap ol {\n padding: 0 1rem;\n margin: 1.25rem 1rem 1.25rem 0.4rem;\n}\n\n.tiptap ul li p,\n.tiptap ol li p {\n margin-top: 0.25em;\n margin-bottom: 0.25em;\n}\n\n/* Heading styles */\n.tiptap h1,\n.tiptap h2,\n.tiptap h3,\n.tiptap h4,\n.tiptap h5,\n.tiptap h6 {\n line-height: 1.1;\n margin-top: 2.5rem;\n text-wrap: pretty;\n font-weight: bold;\n}\n\n.tiptap h1,\n.tiptap h2,\n.tiptap h3,\n.tiptap h4,\n.tiptap h5,\n.tiptap h6 {\n margin-top: 3.5rem;\n margin-bottom: 1.5rem;\n}\n\n.tiptap p {\n margin-bottom: 1rem;\n}\n\n.tiptap h1 {\n font-size: 1.4rem;\n}\n\n.tiptap h2 {\n font-size: 1.2rem;\n}\n\n.tiptap h3 {\n font-size: 1.1rem;\n}\n\n.tiptap h4,\n.tiptap h5,\n.tiptap h6 {\n font-size: 1rem;\n}\n\n/* Code and preformatted text styles */\n.tiptap code {\n background-color: var(--purple-light);\n border-radius: 0.4rem;\n color: var(--black);\n font-size: 0.85rem;\n padding: 0.25em 0.3em;\n}\n\n.tiptap pre {\n background: var(--black);\n border-radius: 0.5rem;\n color: var(--white);\n font-family: \"JetBrainsMono\", monospace;\n margin: 1.5rem 0;\n padding: 0.75rem 1rem;\n}\n\n.tiptap pre code {\n background: none;\n color: inherit;\n font-size: 0.8rem;\n padding: 0;\n}\n\n.tiptap blockquote {\n border-left: 3px solid var(--gray-3);\n margin: 1.5rem 0;\n padding-left: 1rem;\n}\n\n.tiptap hr {\n border: none;\n border-top: 1px solid var(--gray-2);\n margin: 2rem 0;\n}\n\n.tiptap s {\n background-color: #f9818150;\n padding: 2px;\n font-weight: bold;\n color: rgba(0, 0, 0, 0.7);\n}\n\n.tiptap em {\n background-color: #b2f2bb;\n padding: 2px;\n font-weight: bold;\n font-style: normal;\n}\n\n.copilotKitWindow {\n box-shadow: 0 2px 10px rgba(0, 0, 0, 0.1);\n}\n\n", + "language": "css", + "type": "file" + }, + { + "name": "README.mdx", + "content": "# 📝 Predictive State Updates Document Editor\n\n## What This Demo Shows\n\nThis demo showcases CopilotKit's **predictive state updates** for real-time\ndocument collaboration:\n\n1. **Live Document Editing**: Watch as your Copilot makes changes to a document\n in real-time\n2. **Diff Visualization**: See exactly what's being changed as it happens\n3. **Streaming Updates**: Changes are displayed character-by-character as the\n Copilot works\n\n## How to Interact\n\nTry these interactions with the collaborative document editor:\n\n- \"Fix the grammar and typos in this document\"\n- \"Make this text more professional\"\n- \"Add a section about [topic]\"\n- \"Summarize this content in bullet points\"\n- \"Change the tone to be more casual\"\n\nWatch as the Copilot processes your request and edits the document in real-time\nright before your eyes.\n\n## ✨ Predictive State Updates in Action\n\n**What's happening technically:**\n\n- The document state is shared between your UI and the Copilot\n- As the Copilot generates content, changes are streamed to the UI\n- Each modification is visualized with additions and deletions\n- The UI renders these changes progressively, without waiting for completion\n- All edits are tracked and displayed in a visually intuitive way\n\n**What you'll see in this demo:**\n\n- Text changes are highlighted in different colors (green for additions, red for\n deletions)\n- The document updates character-by-character, creating a typing-like effect\n- You can see the Copilot's thought process as it refines the content\n- The final document seamlessly incorporates all changes\n- The experience feels collaborative, as if someone is editing alongside you\n\nThis pattern of real-time collaborative editing with diff visualization is\nperfect for document editors, code review tools, content creation platforms, or\nany application where users benefit from seeing exactly how content is being\ntransformed!\n", + "language": "markdown", + "type": "file" + }, + { + "name": "endpoint.py", + "content": "\"\"\"\nAG-UI FastAPI server for SuperOptiX.\n\"\"\"\nimport asyncio\nimport uuid\nfrom typing import List, Optional, Dict, Any\nfrom pathlib import Path\n\nfrom fastapi import FastAPI, Request\nfrom fastapi.responses import StreamingResponse\n\nfrom ag_ui.core import (\n RunAgentInput,\n EventType,\n RunStartedEvent,\n RunFinishedEvent,\n RunErrorEvent,\n Message,\n Tool\n)\nfrom ag_ui.core.events import (\n TextMessageChunkEvent,\n ToolCallChunkEvent,\n StepStartedEvent,\n StepFinishedEvent,\n MessagesSnapshotEvent,\n StateSnapshotEvent,\n CustomEvent,\n)\nfrom ag_ui.encoder import EventEncoder\n\nfrom superoptix.runners.dspy_runner import DSPyRunner\n\nQUEUES = {}\nQUEUES_LOCK = asyncio.Lock()\n\n\nasync def create_queue(runner: DSPyRunner) -> asyncio.Queue:\n \"\"\"Create a queue for a runner.\"\"\"\n queue_id = id(runner)\n async with QUEUES_LOCK:\n queue = asyncio.Queue()\n QUEUES[queue_id] = queue\n return queue\n\n\ndef get_queue(runner: DSPyRunner) -> Optional[asyncio.Queue]:\n \"\"\"Get the queue for a runner.\"\"\"\n queue_id = id(runner)\n return QUEUES.get(queue_id)\n\n\nasync def delete_queue(runner: DSPyRunner) -> None:\n \"\"\"Delete the queue for a runner.\"\"\"\n queue_id = id(runner)\n async with QUEUES_LOCK:\n if queue_id in QUEUES:\n del QUEUES[queue_id]\n\n\ndef superoptix_prepare_inputs(\n *,\n state: dict,\n messages: List[Message],\n tools: List[Tool],\n) -> Dict[str, Any]:\n \"\"\"Prepare inputs for SuperOptiX agent.\"\"\"\n # Convert AG-UI messages to SuperOptiX format\n messages_data = [message.model_dump() for message in messages]\n \n # Remove system message if present\n if len(messages_data) > 0:\n if \"role\" in messages_data[0] and messages_data[0][\"role\"] == \"system\":\n messages_data = messages[1:]\n \n # Convert tools to SuperOptiX format\n tools_data = [tool.model_dump() for tool in tools]\n \n # Prepare state for SuperOptiX\n new_state = {\n **state,\n \"messages\": messages_data,\n \"tools\": tools_data\n }\n \n return new_state\n\n\ndef add_superoptix_fastapi_endpoint(\n app: FastAPI, \n agent_name: str, \n project_root: Optional[Path] = None,\n path: str = \"/\"\n):\n \"\"\"Adds a SuperOptiX endpoint to the FastAPI app.\"\"\"\n \n @app.post(path)\n async def agentic_chat_endpoint(input_data: RunAgentInput, request: Request):\n \"\"\"SuperOptiX agentic chat endpoint\"\"\"\n \n # Initialize SuperOptiX runner\n runner = DSPyRunner(\n agent_name=agent_name,\n project_root=project_root\n )\n \n # Get the accept header from the request\n accept_header = request.headers.get(\"accept\")\n \n # Create an event encoder to properly format SSE events\n encoder = EventEncoder(accept=accept_header)\n \n # Prepare inputs for SuperOptiX\n inputs = superoptix_prepare_inputs(\n state=input_data.state,\n messages=input_data.messages,\n tools=input_data.tools,\n )\n \n # Extract the main query from messages\n query = \"\"\n if input_data.messages:\n # Get the last user message as the query\n for message in reversed(input_data.messages):\n if message.role == \"user\":\n query = message.content or \"\"\n break\n \n async def event_generator():\n try:\n # Send run started event\n yield encoder.encode(\n RunStartedEvent(\n type=EventType.RUN_STARTED,\n thread_id=input_data.thread_id,\n run_id=input_data.run_id\n )\n )\n \n # Send step started event\n step_id = str(uuid.uuid4())\n yield encoder.encode(\n StepStartedEvent(\n type=EventType.STEP_STARTED,\n step_id=step_id,\n name=\"superoptix_agent_execution\"\n )\n )\n \n # Run the SuperOptiX agent\n result = await runner.run(query=query, **inputs)\n \n # Send step finished event\n yield encoder.encode(\n StepFinishedEvent(\n type=EventType.STEP_FINISHED,\n step_id=step_id\n )\n )\n \n # Send the result as a text message\n message_id = str(uuid.uuid4())\n \n # Start message\n yield encoder.encode({\n \"type\": EventType.TEXT_MESSAGE_START,\n \"message_id\": message_id,\n \"role\": \"assistant\"\n })\n \n # Send content in chunks\n if isinstance(result, dict):\n content = result.get(\"implementation\", str(result))\n else:\n content = str(result)\n \n # Split content into chunks for streaming\n chunk_size = 50\n for i in range(0, len(content), chunk_size):\n chunk = content[i:i + chunk_size]\n yield encoder.encode({\n \"type\": EventType.TEXT_MESSAGE_CHUNK,\n \"message_id\": message_id,\n \"delta\": chunk\n })\n \n # End message\n yield encoder.encode({\n \"type\": EventType.TEXT_MESSAGE_END,\n \"message_id\": message_id\n })\n \n # Send run finished event\n yield encoder.encode(\n RunFinishedEvent(\n type=EventType.RUN_FINISHED,\n thread_id=input_data.thread_id,\n run_id=input_data.run_id\n )\n )\n \n except Exception as error:\n yield encoder.encode(\n RunErrorEvent(\n type=EventType.RUN_ERROR,\n thread_id=input_data.thread_id,\n run_id=input_data.run_id,\n error=str(error)\n )\n )\n \n return StreamingResponse(\n event_generator(),\n media_type=encoder.get_content_type()\n ) ", + "language": "python", + "type": "file" + } ] } \ No newline at end of file diff --git a/typescript-sdk/apps/dojo/src/menu.ts b/typescript-sdk/apps/dojo/src/menu.ts index 36a378c99..5f978c44d 100644 --- a/typescript-sdk/apps/dojo/src/menu.ts +++ b/typescript-sdk/apps/dojo/src/menu.ts @@ -96,4 +96,16 @@ export const menuIntegrations: MenuIntegrationConfig[] = [ "predictive_state_updates", ], }, + { + id: "superoptix", + name: "SuperOptiX", + features: [ + "agentic_chat", + "human_in_the_loop", + "tool_based_generative_ui", + "agentic_generative_ui", + "shared_state", + "predictive_state_updates", + ], + }, ]; diff --git a/typescript-sdk/integrations/superoptix/.npmignore b/typescript-sdk/integrations/superoptix/.npmignore new file mode 100644 index 000000000..4c0bc47bc --- /dev/null +++ b/typescript-sdk/integrations/superoptix/.npmignore @@ -0,0 +1,13 @@ +.turbo +.DS_Store +.git +.gitignore +.idea +.vscode +.env +__tests__ +src +tsup.config.ts +tsconfig.json +jest.config.js +server diff --git a/typescript-sdk/integrations/superoptix/README.md b/typescript-sdk/integrations/superoptix/README.md new file mode 100644 index 000000000..02e7b6ad6 --- /dev/null +++ b/typescript-sdk/integrations/superoptix/README.md @@ -0,0 +1,141 @@ +# SuperOptiX AG-UI Integration + +This integration enables [SuperOptiX](https://superoptix.ai) agents to work seamlessly with the AG-UI ecosystem. SuperOptiX is a [DSPy](https://dspy.ai)-powered agent framework for agent development and optimization. + +## 🚀 Quick Start + +### Prerequisites +- Python 3.12+ +- Node.js 18+ +- SuperOptiX CLI + +### Installation + +1. **Install SuperOptiX CLI:** + ```bash + pip install superoptix + ``` + +2. **Install AG-UI SuperOptiX packages:** + ```bash + # Install Python package + cd typescript-sdk/integrations/superoptix/python + pip install -e . + + # Install TypeScript package (from typescript-sdk root) + cd ../../.. + pnpm install + ``` + +### Setup SuperOptiX Project + +1. **Create a SuperOptiX project:** + ```bash + super init swe + cd swe + ``` + +2. **Pull and compile an agent:** + ```bash + super agent pull developer + super agent compile developer + ``` + +3. **Configure the server:** + - Edit `typescript-sdk/integrations/superoptix/python/example_server.py` + - Update `PROJECT_ROOT` to point to your SuperOptiX project + - Update `AGENT_NAME` if using a different agent + +### Running the Integration + +1. **Start the SuperOptiX server:** + ```bash + cd typescript-sdk/integrations/superoptix/python + python example_server.py + ``` + +2. **Start the AG-UI dojo:** + ```bash + cd typescript-sdk/apps/dojo + pnpm dev + ``` + +3. **Test the integration:** + - Open `http://localhost:3000/superoptix/feature/agentic_chat` + - Start chatting with your SuperOptiX agent! + +## 📁 Project Structure + +``` +typescript-sdk/integrations/superoptix/ +├── python/ +│ ├── example_server.py # AG-UI server with setup instructions +│ ├── ag_ui_superoptix/ # AG-UI integration package +│ └── README.md # This file +├── src/ # TypeScript SDK +└── README.md # Integration documentation +``` + +## 🔧 Configuration + +### Server Configuration + +The `example_server.py` file contains configuration options: + +```python +# Update these paths to match your setup +PROJECT_ROOT = Path("/path/to/your/superoptix/project") +AGENT_NAME = "developer" # Your agent name +``` + +### Environment Variables + +- `PORT`: Server port (default: 8000) +- `SUPEROPTIX_URL`: SuperOptiX server URL (default: http://localhost:8000) + +## 🎯 Available Features + +Test these SuperOptiX features in the AG-UI dojo: + +- **Agentic Chat**: `http://localhost:3000/superoptix/feature/agentic_chat` +- **Human in the Loop**: `http://localhost:3000/superoptix/feature/human_in_the_loop` +- **Tool-based Generative UI**: `http://localhost:3000/superoptix/feature/tool_based_generative_ui` +- **Agentic Generative UI**: `http://localhost:3000/superoptix/feature/agentic_generative_ui` +- **Shared State**: `http://localhost:3000/superoptix/feature/shared_state` +- **Predictive State Updates**: `http://localhost:3000/superoptix/feature/predictive_state_updates` + +## 🛠️ Troubleshooting + +### Common Issues + +1. **"SuperOptiX project not found"** + - Ensure you ran `super init` and created a project + - Update `PROJECT_ROOT` in `example_server.py` + +2. **"Agent not found"** + - Run `super agent pull developer` and `super agent compile developer` + - Verify the agent name in `AGENT_NAME` + +3. **"404 Not Found" errors** + - Ensure SuperOptiX server is running on port 8000 + - Check that both servers are accessible + +4. **Build errors** + - Run `pnpm build` in typescript-sdk directory + - Ensure all dependencies are installed + +### Debugging + +- Check server logs for Python errors +- Check browser console for JavaScript errors +- Verify SuperOptiX project structure with `.super` file and agents directory + +## 📚 Additional Resources + +- [SuperOptiX Documentation](https://superoptix.ai) +- [AG-UI Documentation](https://docs.ag-ui.com) +- [DSPy Documentation](https://dspy.ai) + +## 🤝 Contributing + +This integration follows the standard AG-UI integration patterns. See the main AG-UI repository for contribution guidelines. diff --git a/typescript-sdk/integrations/superoptix/jest.config.js b/typescript-sdk/integrations/superoptix/jest.config.js new file mode 100644 index 000000000..0521f8d91 --- /dev/null +++ b/typescript-sdk/integrations/superoptix/jest.config.js @@ -0,0 +1,10 @@ +/** @type {import('ts-jest').JestConfigWithTsJest} */ +module.exports = { + preset: "ts-jest", + testEnvironment: "node", + testMatch: ["**/*.test.ts"], + passWithNoTests: true, + moduleNameMapper: { + "^@/(.*)$": "/src/$1", + }, +}; diff --git a/typescript-sdk/integrations/superoptix/package.json b/typescript-sdk/integrations/superoptix/package.json new file mode 100644 index 000000000..2cc363430 --- /dev/null +++ b/typescript-sdk/integrations/superoptix/package.json @@ -0,0 +1,40 @@ +{ + "name": "@ag-ui/superoptix", + "author": "Your Name ", + "version": "0.0.1", + "main": "./dist/index.js", + "module": "./dist/index.mjs", + "types": "./dist/index.d.ts", + "sideEffects": false, + "files": [ + "dist/**", + "README.md" + ], + "private": false, + "publishConfig": { + "access": "public" + }, + "scripts": { + "build": "tsup", + "dev": "tsup --watch", + "clean": "rm -rf dist", + "typecheck": "tsc --noEmit", + "test": "jest", + "link:global": "pnpm link --global", + "unlink:global": "pnpm unlink --global" + }, + "dependencies": { + "@ag-ui/client": "workspace:*" + }, + "peerDependencies": { + "rxjs": "7.8.1" + }, + "devDependencies": { + "@types/jest": "^29.5.14", + "@types/node": "^20.11.19", + "jest": "^29.7.0", + "ts-jest": "^29.1.2", + "tsup": "^8.0.2", + "typescript": "^5.3.3" + } +} diff --git a/typescript-sdk/integrations/superoptix/python/README.md b/typescript-sdk/integrations/superoptix/python/README.md new file mode 100644 index 000000000..7a6d43361 --- /dev/null +++ b/typescript-sdk/integrations/superoptix/python/README.md @@ -0,0 +1,69 @@ +# AG-UI SuperOptiX Python Server + +This package provides the Python server implementation for AG-UI integration with SuperOptiX agents. + +## Features + +- FastAPI server for SuperOptiX DSPy pipelines +- AG-UI event streaming +- Tool call handling +- State management + +## Installation + +```bash +poetry install +``` + +## Development + +```bash +poetry run dev +``` + +## Prerequisites + +Your SuperOptiX project must follow the standard SuperOptiX project structure: + +``` +your_project/ +├── .super # Project configuration file +├── your_project_name/ # Project name directory +│ └── agents/ +│ └── your_agent_name/ # Agent directory +│ ├── pipelines/ +│ │ └── your_agent_name_pipeline.py # Pipeline file +│ └── playbook/ +│ └── your_agent_name_playbook.yaml # Playbook file +``` + +## Usage + +### Basic Usage + +```python +from ag_ui_superoptix.endpoint import add_superoptix_fastapi_endpoint +from fastapi import FastAPI + +app = FastAPI() +add_superoptix_fastapi_endpoint(app, agent_name="developer", path="/") +``` + +### Example with swe project + +```python +from pathlib import Path +from fastapi import FastAPI +from ag_ui_superoptix.endpoint import add_superoptix_fastapi_endpoint + +app = FastAPI() + +# Point to the swe project directory +project_root = Path(__file__).parent.parent.parent.parent.parent / "swe" +add_superoptix_fastapi_endpoint( + app, + agent_name="developer", # Matches swe/swe/agents/developer/ + project_root=project_root, + path="/" +) +``` \ No newline at end of file diff --git a/typescript-sdk/integrations/superoptix/python/ag_ui_superoptix/__init__.py b/typescript-sdk/integrations/superoptix/python/ag_ui_superoptix/__init__.py new file mode 100644 index 000000000..2bf0b0d96 --- /dev/null +++ b/typescript-sdk/integrations/superoptix/python/ag_ui_superoptix/__init__.py @@ -0,0 +1,8 @@ +""" +AG-UI integration for SuperOptiX agents. +""" + +from .endpoint import add_superoptix_fastapi_endpoint + +__version__ = "0.0.1" +__all__ = ["add_superoptix_fastapi_endpoint"] \ No newline at end of file diff --git a/typescript-sdk/integrations/superoptix/python/ag_ui_superoptix/endpoint.py b/typescript-sdk/integrations/superoptix/python/ag_ui_superoptix/endpoint.py new file mode 100644 index 000000000..5cf2e53b1 --- /dev/null +++ b/typescript-sdk/integrations/superoptix/python/ag_ui_superoptix/endpoint.py @@ -0,0 +1,215 @@ +""" +AG-UI FastAPI server for SuperOptiX. +""" +import asyncio +import uuid +from typing import List, Optional, Dict, Any +from pathlib import Path + +from fastapi import FastAPI, Request +from fastapi.responses import StreamingResponse + +from ag_ui.core import ( + RunAgentInput, + EventType, + RunStartedEvent, + RunFinishedEvent, + RunErrorEvent, + Message, + Tool +) +from ag_ui.core.events import ( + TextMessageChunkEvent, + ToolCallChunkEvent, + StepStartedEvent, + StepFinishedEvent, + MessagesSnapshotEvent, + StateSnapshotEvent, + CustomEvent, +) +from ag_ui.encoder import EventEncoder + +from superoptix.runners.dspy_runner import DSPyRunner + +QUEUES = {} +QUEUES_LOCK = asyncio.Lock() + + +async def create_queue(runner: DSPyRunner) -> asyncio.Queue: + """Create a queue for a runner.""" + queue_id = id(runner) + async with QUEUES_LOCK: + queue = asyncio.Queue() + QUEUES[queue_id] = queue + return queue + + +def get_queue(runner: DSPyRunner) -> Optional[asyncio.Queue]: + """Get the queue for a runner.""" + queue_id = id(runner) + return QUEUES.get(queue_id) + + +async def delete_queue(runner: DSPyRunner) -> None: + """Delete the queue for a runner.""" + queue_id = id(runner) + async with QUEUES_LOCK: + if queue_id in QUEUES: + del QUEUES[queue_id] + + +def superoptix_prepare_inputs( + *, + state: dict, + messages: List[Message], + tools: List[Tool], +) -> Dict[str, Any]: + """Prepare inputs for SuperOptiX agent.""" + # Convert AG-UI messages to SuperOptiX format + messages_data = [message.model_dump() for message in messages] + + # Remove system message if present + if len(messages_data) > 0: + if "role" in messages_data[0] and messages_data[0]["role"] == "system": + messages_data = messages[1:] + + # Convert tools to SuperOptiX format + tools_data = [tool.model_dump() for tool in tools] + + # Prepare state for SuperOptiX + new_state = { + **state, + "messages": messages_data, + "tools": tools_data + } + + return new_state + + +def add_superoptix_fastapi_endpoint( + app: FastAPI, + agent_name: str, + project_root: Optional[Path] = None, + path: str = "/" +): + """Adds a SuperOptiX endpoint to the FastAPI app.""" + + @app.post(path) + async def agentic_chat_endpoint(input_data: RunAgentInput, request: Request): + """SuperOptiX agentic chat endpoint""" + + # Initialize SuperOptiX runner + runner = DSPyRunner( + agent_name=agent_name, + project_root=project_root + ) + + # Get the accept header from the request + accept_header = request.headers.get("accept") + + # Create an event encoder to properly format SSE events + encoder = EventEncoder(accept=accept_header) + + # Prepare inputs for SuperOptiX + inputs = superoptix_prepare_inputs( + state=input_data.state, + messages=input_data.messages, + tools=input_data.tools, + ) + + # Extract the main query from messages + query = "" + if input_data.messages: + # Get the last user message as the query + for message in reversed(input_data.messages): + if message.role == "user": + query = message.content or "" + break + + async def event_generator(): + try: + # Send run started event + yield encoder.encode( + RunStartedEvent( + type=EventType.RUN_STARTED, + thread_id=input_data.thread_id, + run_id=input_data.run_id + ) + ) + + # Send step started event + step_id = str(uuid.uuid4()) + yield encoder.encode( + StepStartedEvent( + type=EventType.STEP_STARTED, + step_id=step_id, + name="superoptix_agent_execution" + ) + ) + + # Run the SuperOptiX agent + result = await runner.run(query=query, **inputs) + + # Send step finished event + yield encoder.encode( + StepFinishedEvent( + type=EventType.STEP_FINISHED, + step_id=step_id + ) + ) + + # Send the result as a text message + message_id = str(uuid.uuid4()) + + # Start message + yield encoder.encode({ + "type": EventType.TEXT_MESSAGE_START, + "message_id": message_id, + "role": "assistant" + }) + + # Send content in chunks + if isinstance(result, dict): + content = result.get("implementation", str(result)) + else: + content = str(result) + + # Split content into chunks for streaming + chunk_size = 50 + for i in range(0, len(content), chunk_size): + chunk = content[i:i + chunk_size] + yield encoder.encode({ + "type": EventType.TEXT_MESSAGE_CHUNK, + "message_id": message_id, + "delta": chunk + }) + + # End message + yield encoder.encode({ + "type": EventType.TEXT_MESSAGE_END, + "message_id": message_id + }) + + # Send run finished event + yield encoder.encode( + RunFinishedEvent( + type=EventType.RUN_FINISHED, + thread_id=input_data.thread_id, + run_id=input_data.run_id + ) + ) + + except Exception as error: + yield encoder.encode( + RunErrorEvent( + type=EventType.RUN_ERROR, + thread_id=input_data.thread_id, + run_id=input_data.run_id, + error=str(error) + ) + ) + + return StreamingResponse( + event_generator(), + media_type=encoder.get_content_type() + ) \ No newline at end of file diff --git a/typescript-sdk/integrations/superoptix/python/example_server.py b/typescript-sdk/integrations/superoptix/python/example_server.py new file mode 100644 index 000000000..b1733e6f6 --- /dev/null +++ b/typescript-sdk/integrations/superoptix/python/example_server.py @@ -0,0 +1,109 @@ +""" +Example SuperOptiX AG-UI server. + +SETUP INSTRUCTIONS: +1. Install SuperOptiX: pip install superoptix +2. Create a SuperOptiX project: super init swe +3. Pull and compile an agent: + cd swe + super agent pull developer + super agent compile developer +4. Update the PROJECT_ROOT path below to point to your project +5. Run this server: python example_server.py +""" +import os +import uvicorn +from pathlib import Path +from fastapi import FastAPI +from dotenv import load_dotenv + +from ag_ui_superoptix.endpoint import add_superoptix_fastapi_endpoint + +load_dotenv() + +app = FastAPI(title="SuperOptiX AG-UI Server") + +# CONFIGURATION: Update this path to point to your SuperOptiX project +# The project should contain a .super file and agents directory +PROJECT_ROOT = Path("/path/to/your/superoptix/project") # UPDATE THIS PATH + +# Example paths (uncomment one): +# PROJECT_ROOT = Path("/Users/username/superoptix/swe") # macOS/Linux +# PROJECT_ROOT = Path("C:/Users/username/superoptix/swe") # Windows +# PROJECT_ROOT = Path(__file__).parent.parent / "swe" # Relative to this file + +# Verify the project exists +if not PROJECT_ROOT.exists(): + raise FileNotFoundError( + f"SuperOptiX project not found at {PROJECT_ROOT}. " + "Please update PROJECT_ROOT in this file to point to your SuperOptiX project. " + "See setup instructions at the top of this file." + ) + +if not (PROJECT_ROOT / ".super").exists(): + raise FileNotFoundError( + f"SuperOptiX project at {PROJECT_ROOT} is missing .super file. " + "Please ensure you ran 'super init' and 'super agent pull developer'." + ) + +# Add SuperOptiX endpoints for different features +# The agent name should match what's available in your project +AGENT_NAME = "developer" # Update this to match your agent name + +add_superoptix_fastapi_endpoint( + app, + agent_name=AGENT_NAME, + project_root=PROJECT_ROOT, + path="/agentic_chat" +) + +add_superoptix_fastapi_endpoint( + app, + agent_name=AGENT_NAME, + project_root=PROJECT_ROOT, + path="/human_in_the_loop" +) + +add_superoptix_fastapi_endpoint( + app, + agent_name=AGENT_NAME, + project_root=PROJECT_ROOT, + path="/tool_based_generative_ui" +) + +add_superoptix_fastapi_endpoint( + app, + agent_name=AGENT_NAME, + project_root=PROJECT_ROOT, + path="/agentic_generative_ui" +) + +add_superoptix_fastapi_endpoint( + app, + agent_name=AGENT_NAME, + project_root=PROJECT_ROOT, + path="/shared_state" +) + +add_superoptix_fastapi_endpoint( + app, + agent_name=AGENT_NAME, + project_root=PROJECT_ROOT, + path="/predictive_state_updates" +) + +def main(): + """Run the uvicorn server.""" + port = int(os.getenv("PORT", "8000")) + print(f"🚀 Starting SuperOptiX AG-UI server on http://localhost:{port}") + print(f"📁 Using SuperOptiX project: {PROJECT_ROOT}") + print(f"🤖 Using agent: {AGENT_NAME}") + uvicorn.run( + "example_server:app", + host="0.0.0.0", + port=port, + reload=True + ) + +if __name__ == "__main__": + main() \ No newline at end of file diff --git a/typescript-sdk/integrations/superoptix/python/example_server_advanced.py b/typescript-sdk/integrations/superoptix/python/example_server_advanced.py new file mode 100644 index 000000000..614376448 --- /dev/null +++ b/typescript-sdk/integrations/superoptix/python/example_server_advanced.py @@ -0,0 +1,53 @@ +""" +Advanced SuperOptiX AG-UI server example. +""" +import os +import uvicorn +from pathlib import Path +from fastapi import FastAPI +from dotenv import load_dotenv + +from ag_ui_superoptix.endpoint import add_superoptix_fastapi_endpoint + +load_dotenv() + +app = FastAPI(title="SuperOptiX AG-UI Server - Advanced Example") + +# Example 1: Using the swe project with developer agent +project_root_swe = Path(__file__).parent.parent.parent.parent.parent / "swe" +add_superoptix_fastapi_endpoint( + app, + agent_name="developer", + project_root=project_root_swe, + path="/developer" +) + +# Example 2: Using a different project (if you have one) +# project_root_other = Path("/path/to/your/superoptix/project") +# add_superoptix_fastapi_endpoint( +# app, +# agent_name="your_agent_name", +# project_root=project_root_other, +# path="/your_agent" +# ) + +# Example 3: Using current directory (if running from a SuperOptiX project) +# add_superoptix_fastapi_endpoint( +# app, +# agent_name="your_agent", +# project_root=Path.cwd(), +# path="/current_project" +# ) + +def main(): + """Run the uvicorn server.""" + port = int(os.getenv("PORT", "8000")) + uvicorn.run( + "example_server_advanced:app", + host="0.0.0.0", + port=port, + reload=True + ) + +if __name__ == "__main__": + main() \ No newline at end of file diff --git a/typescript-sdk/integrations/superoptix/python/pyproject.toml b/typescript-sdk/integrations/superoptix/python/pyproject.toml new file mode 100644 index 000000000..85f368833 --- /dev/null +++ b/typescript-sdk/integrations/superoptix/python/pyproject.toml @@ -0,0 +1,24 @@ +[tool.poetry] +name = "ag-ui-superoptix" +version = "0.0.1" +description = "AG-UI integration for SuperOptiX" +authors = ["SuperOptiX Team "] +readme = "README.md" +packages = [{include = "ag_ui_superoptix"}] + +[tool.poetry.dependencies] +python = "^3.12" +fastapi = "^0.104.1" +uvicorn = "^0.24.0" +ag-ui-protocol = "^0.1.8" +superoptix = "^0.1.0b10" + +[tool.poetry.group.dev.dependencies] +pytest = "^7.4.3" +black = "^23.11.0" +isort = "^5.12.0" +mypy = "^1.7.1" + +[build-system] +requires = ["poetry-core"] +build-backend = "poetry.core.masonry.api" \ No newline at end of file diff --git a/typescript-sdk/integrations/superoptix/server/python/.gitignore b/typescript-sdk/integrations/superoptix/server/python/.gitignore new file mode 100644 index 000000000..c6f079331 --- /dev/null +++ b/typescript-sdk/integrations/superoptix/server/python/.gitignore @@ -0,0 +1,140 @@ +# Byte-compiled / optimized / DLL files +__pycache__/ +*.py[cod] +*$py.class + +# C extensions +*.so + +# Distribution / packaging +.Python +build/ +develop-eggs/ +dist/ +downloads/ +eggs/ +.eggs/ +lib/ +lib64/ +parts/ +sdist/ +var/ +wheels/ +pip-wheel-metadata/ +share/python-wheels/ +*.egg-info/ +.installed.cfg +*.egg +MANIFEST + +# PyInstaller +# Usually these files are written by a python script from a template +# before PyInstaller builds the exe, so as to inject date/other infos into it. +*.manifest +*.spec + +# Installer logs +pip-log.txt +pip-delete-this-directory.txt + +# Unit test / coverage reports +htmlcov/ +.tox/ +.nox/ +.coverage +.coverage.* +.cache +nosetests.xml +coverage.xml +*.cover +*.py,cover +.hypothesis/ +.pytest_cache/ + +# Translations +*.mo +*.pot + +# Django stuff: +*.log +local_settings.py +db.sqlite3 +db.sqlite3-journal + +# Flask stuff: +instance/ +.webassets-cache + +# Scrapy stuff: +.scrapy + +# Sphinx documentation +docs/_build/ + +# PyBuilder +target/ + +# Jupyter Notebook +.ipynb_checkpoints + +# IPython +profile_default/ +ipython_config.py + +# pyenv +.python-version + +# pipenv +# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. +# However, in case of collaboration, if having platform-specific dependencies or dependencies +# having no cross-platform support, pipenv may install dependencies that don't work, or not +# install all needed dependencies. +#Pipfile.lock + +# PEP 582; used by e.g. github.com/David-OConnor/pyflow +__pypackages__/ + +# Celery stuff +celerybeat-schedule +celerybeat.pid + +# SageMath parsed files +*.sage.py + +# Environments +.env +.venv +env/ +venv/ +ENV/ +env.bak/ +venv.bak/ + +# Spyder project settings +.spyderproject +.spyproject + +# Rope project settings +.ropeproject + +# mkdocs documentation +/site + +# mypy +.mypy_cache/ +.dmypy.json +dmypy.json + +# Pyre type checker +.pyre/ + +# IDE +.vscode/ +.idea/ +*.swp +*.swo +*~ + +# OS +.DS_Store +Thumbs.db diff --git a/typescript-sdk/integrations/superoptix/server/python/README.md b/typescript-sdk/integrations/superoptix/server/python/README.md new file mode 100644 index 000000000..e69de29bb diff --git a/typescript-sdk/integrations/superoptix/server/python/example_server/__init__.py b/typescript-sdk/integrations/superoptix/server/python/example_server/__init__.py new file mode 100644 index 000000000..2e2413568 --- /dev/null +++ b/typescript-sdk/integrations/superoptix/server/python/example_server/__init__.py @@ -0,0 +1,90 @@ +""" +Example server for the AG-UI protocol. +""" + +import os +import uvicorn +import uuid +from fastapi import FastAPI, Request +from fastapi.responses import StreamingResponse +from ag_ui.core import ( + RunAgentInput, + EventType, + RunStartedEvent, + RunFinishedEvent, + TextMessageStartEvent, + TextMessageContentEvent, + TextMessageEndEvent, +) +from ag_ui.encoder import EventEncoder + +app = FastAPI(title="AG-UI Endpoint") + +@app.post("/") +async def agentic_chat_endpoint(input_data: RunAgentInput, request: Request): + """Agentic chat endpoint""" + # Get the accept header from the request + accept_header = request.headers.get("accept") + + # Create an event encoder to properly format SSE events + encoder = EventEncoder(accept=accept_header) + + async def event_generator(): + + # Send run started event + yield encoder.encode( + RunStartedEvent( + type=EventType.RUN_STARTED, + thread_id=input_data.thread_id, + run_id=input_data.run_id + ), + ) + + message_id = str(uuid.uuid4()) + + yield encoder.encode( + TextMessageStartEvent( + type=EventType.TEXT_MESSAGE_START, + message_id=message_id, + role="assistant" + ) + ) + + yield encoder.encode( + TextMessageContentEvent( + type=EventType.TEXT_MESSAGE_CONTENT, + message_id=message_id, + delta="Hello world!" + ) + ) + + yield encoder.encode( + TextMessageEndEvent( + type=EventType.TEXT_MESSAGE_END, + message_id=message_id + ) + ) + + # Send run finished event + yield encoder.encode( + RunFinishedEvent( + type=EventType.RUN_FINISHED, + thread_id=input_data.thread_id, + run_id=input_data.run_id + ), + ) + + return StreamingResponse( + event_generator(), + media_type=encoder.get_content_type() + ) + +def main(): + """Run the uvicorn server.""" + port = int(os.getenv("PORT", "8000")) + uvicorn.run( + "example_server:app", + host="0.0.0.0", + port=port, + reload=True + ) diff --git a/typescript-sdk/integrations/superoptix/server/python/poetry.lock b/typescript-sdk/integrations/superoptix/server/python/poetry.lock new file mode 100644 index 000000000..7b4db84c2 --- /dev/null +++ b/typescript-sdk/integrations/superoptix/server/python/poetry.lock @@ -0,0 +1,325 @@ +# This file is automatically @generated by Poetry 1.8.3 and should not be changed by hand. + +[[package]] +name = "ag-ui-protocol" +version = "0.1.5" +description = "" +optional = false +python-versions = "<4.0,>=3.9" +files = [ + {file = "ag_ui_protocol-0.1.5-py3-none-any.whl", hash = "sha256:d51a0ad9635059b629b4cb57a9a2ec425b4cc8220e91d50a8f9d559571737ae9"}, + {file = "ag_ui_protocol-0.1.5.tar.gz", hash = "sha256:48757afe82a4ee88eb078f31ef9672e09df624573d82045054f5a5b5dc021832"}, +] + +[package.dependencies] +pydantic = ">=2.11.2,<3.0.0" + +[[package]] +name = "annotated-types" +version = "0.7.0" +description = "Reusable constraint types to use with typing.Annotated" +optional = false +python-versions = ">=3.8" +files = [ + {file = "annotated_types-0.7.0-py3-none-any.whl", hash = "sha256:1f02e8b43a8fbbc3f3e0d4f0f4bfc8131bcb4eebe8849b8e5c773f3a1c582a53"}, + {file = "annotated_types-0.7.0.tar.gz", hash = "sha256:aff07c09a53a08bc8cfccb9c85b05f1aa9a2a6f23728d790723543408344ce89"}, +] + +[[package]] +name = "anyio" +version = "4.9.0" +description = "High level compatibility layer for multiple asynchronous event loop implementations" +optional = false +python-versions = ">=3.9" +files = [ + {file = "anyio-4.9.0-py3-none-any.whl", hash = "sha256:9f76d541cad6e36af7beb62e978876f3b41e3e04f2c1fbf0884604c0a9c4d93c"}, + {file = "anyio-4.9.0.tar.gz", hash = "sha256:673c0c244e15788651a4ff38710fea9675823028a6f08a5eda409e0c9840a028"}, +] + +[package.dependencies] +idna = ">=2.8" +sniffio = ">=1.1" +typing_extensions = {version = ">=4.5", markers = "python_version < \"3.13\""} + +[package.extras] +doc = ["Sphinx (>=8.2,<9.0)", "packaging", "sphinx-autodoc-typehints (>=1.2.0)", "sphinx_rtd_theme"] +test = ["anyio[trio]", "blockbuster (>=1.5.23)", "coverage[toml] (>=7)", "exceptiongroup (>=1.2.0)", "hypothesis (>=4.0)", "psutil (>=5.9)", "pytest (>=7.0)", "trustme", "truststore (>=0.9.1)", "uvloop (>=0.21)"] +trio = ["trio (>=0.26.1)"] + +[[package]] +name = "click" +version = "8.2.1" +description = "Composable command line interface toolkit" +optional = false +python-versions = ">=3.10" +files = [ + {file = "click-8.2.1-py3-none-any.whl", hash = "sha256:61a3265b914e850b85317d0b3109c7f8cd35a670f963866005d6ef1d5175a12b"}, + {file = "click-8.2.1.tar.gz", hash = "sha256:27c491cc05d968d271d5a1db13e3b5a184636d9d930f148c50b038f0d0646202"}, +] + +[package.dependencies] +colorama = {version = "*", markers = "platform_system == \"Windows\""} + +[[package]] +name = "colorama" +version = "0.4.6" +description = "Cross-platform colored terminal text." +optional = false +python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,!=3.6.*,>=2.7" +files = [ + {file = "colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6"}, + {file = "colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44"}, +] + +[[package]] +name = "fastapi" +version = "0.115.12" +description = "FastAPI framework, high performance, easy to learn, fast to code, ready for production" +optional = false +python-versions = ">=3.8" +files = [ + {file = "fastapi-0.115.12-py3-none-any.whl", hash = "sha256:e94613d6c05e27be7ffebdd6ea5f388112e5e430c8f7d6494a9d1d88d43e814d"}, + {file = "fastapi-0.115.12.tar.gz", hash = "sha256:1e2c2a2646905f9e83d32f04a3f86aff4a286669c6c950ca95b5fd68c2602681"}, +] + +[package.dependencies] +pydantic = ">=1.7.4,<1.8 || >1.8,<1.8.1 || >1.8.1,<2.0.0 || >2.0.0,<2.0.1 || >2.0.1,<2.1.0 || >2.1.0,<3.0.0" +starlette = ">=0.40.0,<0.47.0" +typing-extensions = ">=4.8.0" + +[package.extras] +all = ["email-validator (>=2.0.0)", "fastapi-cli[standard] (>=0.0.5)", "httpx (>=0.23.0)", "itsdangerous (>=1.1.0)", "jinja2 (>=3.1.5)", "orjson (>=3.2.1)", "pydantic-extra-types (>=2.0.0)", "pydantic-settings (>=2.0.0)", "python-multipart (>=0.0.18)", "pyyaml (>=5.3.1)", "ujson (>=4.0.1,!=4.0.2,!=4.1.0,!=4.2.0,!=4.3.0,!=5.0.0,!=5.1.0)", "uvicorn[standard] (>=0.12.0)"] +standard = ["email-validator (>=2.0.0)", "fastapi-cli[standard] (>=0.0.5)", "httpx (>=0.23.0)", "jinja2 (>=3.1.5)", "python-multipart (>=0.0.18)", "uvicorn[standard] (>=0.12.0)"] + +[[package]] +name = "h11" +version = "0.16.0" +description = "A pure-Python, bring-your-own-I/O implementation of HTTP/1.1" +optional = false +python-versions = ">=3.8" +files = [ + {file = "h11-0.16.0-py3-none-any.whl", hash = "sha256:63cf8bbe7522de3bf65932fda1d9c2772064ffb3dae62d55932da54b31cb6c86"}, + {file = "h11-0.16.0.tar.gz", hash = "sha256:4e35b956cf45792e4caa5885e69fba00bdbc6ffafbfa020300e549b208ee5ff1"}, +] + +[[package]] +name = "idna" +version = "3.10" +description = "Internationalized Domain Names in Applications (IDNA)" +optional = false +python-versions = ">=3.6" +files = [ + {file = "idna-3.10-py3-none-any.whl", hash = "sha256:946d195a0d259cbba61165e88e65941f16e9b36ea6ddb97f00452bae8b1287d3"}, + {file = "idna-3.10.tar.gz", hash = "sha256:12f65c9b470abda6dc35cf8e63cc574b1c52b11df2c86030af0ac09b01b13ea9"}, +] + +[package.extras] +all = ["flake8 (>=7.1.1)", "mypy (>=1.11.2)", "pytest (>=8.3.2)", "ruff (>=0.6.2)"] + +[[package]] +name = "pydantic" +version = "2.11.5" +description = "Data validation using Python type hints" +optional = false +python-versions = ">=3.9" +files = [ + {file = "pydantic-2.11.5-py3-none-any.whl", hash = "sha256:f9c26ba06f9747749ca1e5c94d6a85cb84254577553c8785576fd38fa64dc0f7"}, + {file = "pydantic-2.11.5.tar.gz", hash = "sha256:7f853db3d0ce78ce8bbb148c401c2cdd6431b3473c0cdff2755c7690952a7b7a"}, +] + +[package.dependencies] +annotated-types = ">=0.6.0" +pydantic-core = "2.33.2" +typing-extensions = ">=4.12.2" +typing-inspection = ">=0.4.0" + +[package.extras] +email = ["email-validator (>=2.0.0)"] +timezone = ["tzdata"] + +[[package]] +name = "pydantic-core" +version = "2.33.2" +description = "Core functionality for Pydantic validation and serialization" +optional = false +python-versions = ">=3.9" +files = [ + {file = "pydantic_core-2.33.2-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:2b3d326aaef0c0399d9afffeb6367d5e26ddc24d351dbc9c636840ac355dc5d8"}, + {file = "pydantic_core-2.33.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:0e5b2671f05ba48b94cb90ce55d8bdcaaedb8ba00cc5359f6810fc918713983d"}, + {file = "pydantic_core-2.33.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0069c9acc3f3981b9ff4cdfaf088e98d83440a4c7ea1bc07460af3d4dc22e72d"}, + {file = "pydantic_core-2.33.2-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:d53b22f2032c42eaaf025f7c40c2e3b94568ae077a606f006d206a463bc69572"}, + {file = "pydantic_core-2.33.2-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:0405262705a123b7ce9f0b92f123334d67b70fd1f20a9372b907ce1080c7ba02"}, + {file = "pydantic_core-2.33.2-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:4b25d91e288e2c4e0662b8038a28c6a07eaac3e196cfc4ff69de4ea3db992a1b"}, + {file = "pydantic_core-2.33.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6bdfe4b3789761f3bcb4b1ddf33355a71079858958e3a552f16d5af19768fef2"}, + {file = "pydantic_core-2.33.2-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:efec8db3266b76ef9607c2c4c419bdb06bf335ae433b80816089ea7585816f6a"}, + {file = "pydantic_core-2.33.2-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:031c57d67ca86902726e0fae2214ce6770bbe2f710dc33063187a68744a5ecac"}, + {file = "pydantic_core-2.33.2-cp310-cp310-musllinux_1_1_armv7l.whl", hash = "sha256:f8de619080e944347f5f20de29a975c2d815d9ddd8be9b9b7268e2e3ef68605a"}, + {file = "pydantic_core-2.33.2-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:73662edf539e72a9440129f231ed3757faab89630d291b784ca99237fb94db2b"}, + {file = "pydantic_core-2.33.2-cp310-cp310-win32.whl", hash = "sha256:0a39979dcbb70998b0e505fb1556a1d550a0781463ce84ebf915ba293ccb7e22"}, + {file = "pydantic_core-2.33.2-cp310-cp310-win_amd64.whl", hash = "sha256:b0379a2b24882fef529ec3b4987cb5d003b9cda32256024e6fe1586ac45fc640"}, + {file = "pydantic_core-2.33.2-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:4c5b0a576fb381edd6d27f0a85915c6daf2f8138dc5c267a57c08a62900758c7"}, + {file = "pydantic_core-2.33.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:e799c050df38a639db758c617ec771fd8fb7a5f8eaaa4b27b101f266b216a246"}, + {file = "pydantic_core-2.33.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:dc46a01bf8d62f227d5ecee74178ffc448ff4e5197c756331f71efcc66dc980f"}, + {file = "pydantic_core-2.33.2-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:a144d4f717285c6d9234a66778059f33a89096dfb9b39117663fd8413d582dcc"}, + {file = "pydantic_core-2.33.2-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:73cf6373c21bc80b2e0dc88444f41ae60b2f070ed02095754eb5a01df12256de"}, + {file = "pydantic_core-2.33.2-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:3dc625f4aa79713512d1976fe9f0bc99f706a9dee21dfd1810b4bbbf228d0e8a"}, + {file = "pydantic_core-2.33.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:881b21b5549499972441da4758d662aeea93f1923f953e9cbaff14b8b9565aef"}, + {file = "pydantic_core-2.33.2-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:bdc25f3681f7b78572699569514036afe3c243bc3059d3942624e936ec93450e"}, + {file = "pydantic_core-2.33.2-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:fe5b32187cbc0c862ee201ad66c30cf218e5ed468ec8dc1cf49dec66e160cc4d"}, + {file = "pydantic_core-2.33.2-cp311-cp311-musllinux_1_1_armv7l.whl", hash = "sha256:bc7aee6f634a6f4a95676fcb5d6559a2c2a390330098dba5e5a5f28a2e4ada30"}, + {file = "pydantic_core-2.33.2-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:235f45e5dbcccf6bd99f9f472858849f73d11120d76ea8707115415f8e5ebebf"}, + {file = "pydantic_core-2.33.2-cp311-cp311-win32.whl", hash = "sha256:6368900c2d3ef09b69cb0b913f9f8263b03786e5b2a387706c5afb66800efd51"}, + {file = "pydantic_core-2.33.2-cp311-cp311-win_amd64.whl", hash = "sha256:1e063337ef9e9820c77acc768546325ebe04ee38b08703244c1309cccc4f1bab"}, + {file = "pydantic_core-2.33.2-cp311-cp311-win_arm64.whl", hash = "sha256:6b99022f1d19bc32a4c2a0d544fc9a76e3be90f0b3f4af413f87d38749300e65"}, + {file = "pydantic_core-2.33.2-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:a7ec89dc587667f22b6a0b6579c249fca9026ce7c333fc142ba42411fa243cdc"}, + {file = "pydantic_core-2.33.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:3c6db6e52c6d70aa0d00d45cdb9b40f0433b96380071ea80b09277dba021ddf7"}, + {file = "pydantic_core-2.33.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4e61206137cbc65e6d5256e1166f88331d3b6238e082d9f74613b9b765fb9025"}, + {file = "pydantic_core-2.33.2-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:eb8c529b2819c37140eb51b914153063d27ed88e3bdc31b71198a198e921e011"}, + {file = "pydantic_core-2.33.2-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:c52b02ad8b4e2cf14ca7b3d918f3eb0ee91e63b3167c32591e57c4317e134f8f"}, + {file = "pydantic_core-2.33.2-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:96081f1605125ba0855dfda83f6f3df5ec90c61195421ba72223de35ccfb2f88"}, + {file = "pydantic_core-2.33.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8f57a69461af2a5fa6e6bbd7a5f60d3b7e6cebb687f55106933188e79ad155c1"}, + {file = "pydantic_core-2.33.2-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:572c7e6c8bb4774d2ac88929e3d1f12bc45714ae5ee6d9a788a9fb35e60bb04b"}, + {file = "pydantic_core-2.33.2-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:db4b41f9bd95fbe5acd76d89920336ba96f03e149097365afe1cb092fceb89a1"}, + {file = "pydantic_core-2.33.2-cp312-cp312-musllinux_1_1_armv7l.whl", hash = "sha256:fa854f5cf7e33842a892e5c73f45327760bc7bc516339fda888c75ae60edaeb6"}, + {file = "pydantic_core-2.33.2-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:5f483cfb75ff703095c59e365360cb73e00185e01aaea067cd19acffd2ab20ea"}, + {file = "pydantic_core-2.33.2-cp312-cp312-win32.whl", hash = "sha256:9cb1da0f5a471435a7bc7e439b8a728e8b61e59784b2af70d7c169f8dd8ae290"}, + {file = "pydantic_core-2.33.2-cp312-cp312-win_amd64.whl", hash = "sha256:f941635f2a3d96b2973e867144fde513665c87f13fe0e193c158ac51bfaaa7b2"}, + {file = "pydantic_core-2.33.2-cp312-cp312-win_arm64.whl", hash = "sha256:cca3868ddfaccfbc4bfb1d608e2ccaaebe0ae628e1416aeb9c4d88c001bb45ab"}, + {file = "pydantic_core-2.33.2-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:1082dd3e2d7109ad8b7da48e1d4710c8d06c253cbc4a27c1cff4fbcaa97a9e3f"}, + {file = "pydantic_core-2.33.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:f517ca031dfc037a9c07e748cefd8d96235088b83b4f4ba8939105d20fa1dcd6"}, + {file = "pydantic_core-2.33.2-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0a9f2c9dd19656823cb8250b0724ee9c60a82f3cdf68a080979d13092a3b0fef"}, + {file = "pydantic_core-2.33.2-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:2b0a451c263b01acebe51895bfb0e1cc842a5c666efe06cdf13846c7418caa9a"}, + {file = "pydantic_core-2.33.2-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1ea40a64d23faa25e62a70ad163571c0b342b8bf66d5fa612ac0dec4f069d916"}, + {file = "pydantic_core-2.33.2-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:0fb2d542b4d66f9470e8065c5469ec676978d625a8b7a363f07d9a501a9cb36a"}, + {file = "pydantic_core-2.33.2-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9fdac5d6ffa1b5a83bca06ffe7583f5576555e6c8b3a91fbd25ea7780f825f7d"}, + {file = "pydantic_core-2.33.2-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:04a1a413977ab517154eebb2d326da71638271477d6ad87a769102f7c2488c56"}, + {file = "pydantic_core-2.33.2-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:c8e7af2f4e0194c22b5b37205bfb293d166a7344a5b0d0eaccebc376546d77d5"}, + {file = "pydantic_core-2.33.2-cp313-cp313-musllinux_1_1_armv7l.whl", hash = "sha256:5c92edd15cd58b3c2d34873597a1e20f13094f59cf88068adb18947df5455b4e"}, + {file = "pydantic_core-2.33.2-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:65132b7b4a1c0beded5e057324b7e16e10910c106d43675d9bd87d4f38dde162"}, + {file = "pydantic_core-2.33.2-cp313-cp313-win32.whl", hash = "sha256:52fb90784e0a242bb96ec53f42196a17278855b0f31ac7c3cc6f5c1ec4811849"}, + {file = "pydantic_core-2.33.2-cp313-cp313-win_amd64.whl", hash = "sha256:c083a3bdd5a93dfe480f1125926afcdbf2917ae714bdb80b36d34318b2bec5d9"}, + {file = "pydantic_core-2.33.2-cp313-cp313-win_arm64.whl", hash = "sha256:e80b087132752f6b3d714f041ccf74403799d3b23a72722ea2e6ba2e892555b9"}, + {file = "pydantic_core-2.33.2-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:61c18fba8e5e9db3ab908620af374db0ac1baa69f0f32df4f61ae23f15e586ac"}, + {file = "pydantic_core-2.33.2-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:95237e53bb015f67b63c91af7518a62a8660376a6a0db19b89acc77a4d6199f5"}, + {file = "pydantic_core-2.33.2-cp313-cp313t-win_amd64.whl", hash = "sha256:c2fc0a768ef76c15ab9238afa6da7f69895bb5d1ee83aeea2e3509af4472d0b9"}, + {file = "pydantic_core-2.33.2-cp39-cp39-macosx_10_12_x86_64.whl", hash = "sha256:a2b911a5b90e0374d03813674bf0a5fbbb7741570dcd4b4e85a2e48d17def29d"}, + {file = "pydantic_core-2.33.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:6fa6dfc3e4d1f734a34710f391ae822e0a8eb8559a85c6979e14e65ee6ba2954"}, + {file = "pydantic_core-2.33.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c54c939ee22dc8e2d545da79fc5381f1c020d6d3141d3bd747eab59164dc89fb"}, + {file = "pydantic_core-2.33.2-cp39-cp39-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:53a57d2ed685940a504248187d5685e49eb5eef0f696853647bf37c418c538f7"}, + {file = "pydantic_core-2.33.2-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:09fb9dd6571aacd023fe6aaca316bd01cf60ab27240d7eb39ebd66a3a15293b4"}, + {file = "pydantic_core-2.33.2-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:0e6116757f7959a712db11f3e9c0a99ade00a5bbedae83cb801985aa154f071b"}, + {file = "pydantic_core-2.33.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8d55ab81c57b8ff8548c3e4947f119551253f4e3787a7bbc0b6b3ca47498a9d3"}, + {file = "pydantic_core-2.33.2-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:c20c462aa4434b33a2661701b861604913f912254e441ab8d78d30485736115a"}, + {file = "pydantic_core-2.33.2-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:44857c3227d3fb5e753d5fe4a3420d6376fa594b07b621e220cd93703fe21782"}, + {file = "pydantic_core-2.33.2-cp39-cp39-musllinux_1_1_armv7l.whl", hash = "sha256:eb9b459ca4df0e5c87deb59d37377461a538852765293f9e6ee834f0435a93b9"}, + {file = "pydantic_core-2.33.2-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:9fcd347d2cc5c23b06de6d3b7b8275be558a0c90549495c699e379a80bf8379e"}, + {file = "pydantic_core-2.33.2-cp39-cp39-win32.whl", hash = "sha256:83aa99b1285bc8f038941ddf598501a86f1536789740991d7d8756e34f1e74d9"}, + {file = "pydantic_core-2.33.2-cp39-cp39-win_amd64.whl", hash = "sha256:f481959862f57f29601ccced557cc2e817bce7533ab8e01a797a48b49c9692b3"}, + {file = "pydantic_core-2.33.2-pp310-pypy310_pp73-macosx_10_12_x86_64.whl", hash = "sha256:5c4aa4e82353f65e548c476b37e64189783aa5384903bfea4f41580f255fddfa"}, + {file = "pydantic_core-2.33.2-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:d946c8bf0d5c24bf4fe333af284c59a19358aa3ec18cb3dc4370080da1e8ad29"}, + {file = "pydantic_core-2.33.2-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:87b31b6846e361ef83fedb187bb5b4372d0da3f7e28d85415efa92d6125d6e6d"}, + {file = "pydantic_core-2.33.2-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:aa9d91b338f2df0508606f7009fde642391425189bba6d8c653afd80fd6bb64e"}, + {file = "pydantic_core-2.33.2-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:2058a32994f1fde4ca0480ab9d1e75a0e8c87c22b53a3ae66554f9af78f2fe8c"}, + {file = "pydantic_core-2.33.2-pp310-pypy310_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:0e03262ab796d986f978f79c943fc5f620381be7287148b8010b4097f79a39ec"}, + {file = "pydantic_core-2.33.2-pp310-pypy310_pp73-musllinux_1_1_armv7l.whl", hash = "sha256:1a8695a8d00c73e50bff9dfda4d540b7dee29ff9b8053e38380426a85ef10052"}, + {file = "pydantic_core-2.33.2-pp310-pypy310_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:fa754d1850735a0b0e03bcffd9d4b4343eb417e47196e4485d9cca326073a42c"}, + {file = "pydantic_core-2.33.2-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:a11c8d26a50bfab49002947d3d237abe4d9e4b5bdc8846a63537b6488e197808"}, + {file = "pydantic_core-2.33.2-pp311-pypy311_pp73-macosx_10_12_x86_64.whl", hash = "sha256:dd14041875d09cc0f9308e37a6f8b65f5585cf2598a53aa0123df8b129d481f8"}, + {file = "pydantic_core-2.33.2-pp311-pypy311_pp73-macosx_11_0_arm64.whl", hash = "sha256:d87c561733f66531dced0da6e864f44ebf89a8fba55f31407b00c2f7f9449593"}, + {file = "pydantic_core-2.33.2-pp311-pypy311_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2f82865531efd18d6e07a04a17331af02cb7a651583c418df8266f17a63c6612"}, + {file = "pydantic_core-2.33.2-pp311-pypy311_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2bfb5112df54209d820d7bf9317c7a6c9025ea52e49f46b6a2060104bba37de7"}, + {file = "pydantic_core-2.33.2-pp311-pypy311_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:64632ff9d614e5eecfb495796ad51b0ed98c453e447a76bcbeeb69615079fc7e"}, + {file = "pydantic_core-2.33.2-pp311-pypy311_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:f889f7a40498cc077332c7ab6b4608d296d852182211787d4f3ee377aaae66e8"}, + {file = "pydantic_core-2.33.2-pp311-pypy311_pp73-musllinux_1_1_armv7l.whl", hash = "sha256:de4b83bb311557e439b9e186f733f6c645b9417c84e2eb8203f3f820a4b988bf"}, + {file = "pydantic_core-2.33.2-pp311-pypy311_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:82f68293f055f51b51ea42fafc74b6aad03e70e191799430b90c13d643059ebb"}, + {file = "pydantic_core-2.33.2-pp311-pypy311_pp73-win_amd64.whl", hash = "sha256:329467cecfb529c925cf2bbd4d60d2c509bc2fb52a20c1045bf09bb70971a9c1"}, + {file = "pydantic_core-2.33.2-pp39-pypy39_pp73-macosx_10_12_x86_64.whl", hash = "sha256:87acbfcf8e90ca885206e98359d7dca4bcbb35abdc0ff66672a293e1d7a19101"}, + {file = "pydantic_core-2.33.2-pp39-pypy39_pp73-macosx_11_0_arm64.whl", hash = "sha256:7f92c15cd1e97d4b12acd1cc9004fa092578acfa57b67ad5e43a197175d01a64"}, + {file = "pydantic_core-2.33.2-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d3f26877a748dc4251cfcfda9dfb5f13fcb034f5308388066bcfe9031b63ae7d"}, + {file = "pydantic_core-2.33.2-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:dac89aea9af8cd672fa7b510e7b8c33b0bba9a43186680550ccf23020f32d535"}, + {file = "pydantic_core-2.33.2-pp39-pypy39_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:970919794d126ba8645f3837ab6046fb4e72bbc057b3709144066204c19a455d"}, + {file = "pydantic_core-2.33.2-pp39-pypy39_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:3eb3fe62804e8f859c49ed20a8451342de53ed764150cb14ca71357c765dc2a6"}, + {file = "pydantic_core-2.33.2-pp39-pypy39_pp73-musllinux_1_1_armv7l.whl", hash = "sha256:3abcd9392a36025e3bd55f9bd38d908bd17962cc49bc6da8e7e96285336e2bca"}, + {file = "pydantic_core-2.33.2-pp39-pypy39_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:3a1c81334778f9e3af2f8aeb7a960736e5cab1dfebfb26aabca09afd2906c039"}, + {file = "pydantic_core-2.33.2-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:2807668ba86cb38c6817ad9bc66215ab8584d1d304030ce4f0887336f28a5e27"}, + {file = "pydantic_core-2.33.2.tar.gz", hash = "sha256:7cb8bc3605c29176e1b105350d2e6474142d7c1bd1d9327c4a9bdb46bf827acc"}, +] + +[package.dependencies] +typing-extensions = ">=4.6.0,<4.7.0 || >4.7.0" + +[[package]] +name = "sniffio" +version = "1.3.1" +description = "Sniff out which async library your code is running under" +optional = false +python-versions = ">=3.7" +files = [ + {file = "sniffio-1.3.1-py3-none-any.whl", hash = "sha256:2f6da418d1f1e0fddd844478f41680e794e6051915791a034ff65e5f100525a2"}, + {file = "sniffio-1.3.1.tar.gz", hash = "sha256:f4324edc670a0f49750a81b895f35c3adb843cca46f0530f79fc1babb23789dc"}, +] + +[[package]] +name = "starlette" +version = "0.46.2" +description = "The little ASGI library that shines." +optional = false +python-versions = ">=3.9" +files = [ + {file = "starlette-0.46.2-py3-none-any.whl", hash = "sha256:595633ce89f8ffa71a015caed34a5b2dc1c0cdb3f0f1fbd1e69339cf2abeec35"}, + {file = "starlette-0.46.2.tar.gz", hash = "sha256:7f7361f34eed179294600af672f565727419830b54b7b084efe44bb82d2fccd5"}, +] + +[package.dependencies] +anyio = ">=3.6.2,<5" + +[package.extras] +full = ["httpx (>=0.27.0,<0.29.0)", "itsdangerous", "jinja2", "python-multipart (>=0.0.18)", "pyyaml"] + +[[package]] +name = "typing-extensions" +version = "4.14.0" +description = "Backported and Experimental Type Hints for Python 3.9+" +optional = false +python-versions = ">=3.9" +files = [ + {file = "typing_extensions-4.14.0-py3-none-any.whl", hash = "sha256:a1514509136dd0b477638fc68d6a91497af5076466ad0fa6c338e44e359944af"}, + {file = "typing_extensions-4.14.0.tar.gz", hash = "sha256:8676b788e32f02ab42d9e7c61324048ae4c6d844a399eebace3d4979d75ceef4"}, +] + +[[package]] +name = "typing-inspection" +version = "0.4.1" +description = "Runtime typing introspection tools" +optional = false +python-versions = ">=3.9" +files = [ + {file = "typing_inspection-0.4.1-py3-none-any.whl", hash = "sha256:389055682238f53b04f7badcb49b989835495a96700ced5dab2d8feae4b26f51"}, + {file = "typing_inspection-0.4.1.tar.gz", hash = "sha256:6ae134cc0203c33377d43188d4064e9b357dba58cff3185f22924610e70a9d28"}, +] + +[package.dependencies] +typing-extensions = ">=4.12.0" + +[[package]] +name = "uvicorn" +version = "0.34.3" +description = "The lightning-fast ASGI server." +optional = false +python-versions = ">=3.9" +files = [ + {file = "uvicorn-0.34.3-py3-none-any.whl", hash = "sha256:16246631db62bdfbf069b0645177d6e8a77ba950cfedbfd093acef9444e4d885"}, + {file = "uvicorn-0.34.3.tar.gz", hash = "sha256:35919a9a979d7a59334b6b10e05d77c1d0d574c50e0fc98b8b1a0f165708b55a"}, +] + +[package.dependencies] +click = ">=7.0" +h11 = ">=0.8" + +[package.extras] +standard = ["colorama (>=0.4)", "httptools (>=0.6.3)", "python-dotenv (>=0.13)", "pyyaml (>=5.1)", "uvloop (>=0.15.1)", "watchfiles (>=0.13)", "websockets (>=10.4)"] + +[metadata] +lock-version = "2.0" +python-versions = "^3.12" +content-hash = "44fe994929f6e64eab66f4390efbec99c486511d45f72fb208516b3177ee8ea6" diff --git a/typescript-sdk/integrations/superoptix/server/python/pyproject.toml b/typescript-sdk/integrations/superoptix/server/python/pyproject.toml new file mode 100644 index 000000000..db8b61f8a --- /dev/null +++ b/typescript-sdk/integrations/superoptix/server/python/pyproject.toml @@ -0,0 +1,19 @@ +[tool.poetry] +name = "example_server" +version = "0.1.0" +description = "" +authors = ["Shashi Jagtap "] +readme = "README.md" + +[tool.poetry.dependencies] +python = "^3.12" +ag-ui-protocol = "^0.1.5" +fastapi = "^0.115.12" +uvicorn = "^0.34.3" + +[build-system] +requires = ["poetry-core"] +build-backend = "poetry.core.masonry.api" + +[tool.poetry.scripts] +dev = "example_server:main" \ No newline at end of file diff --git a/typescript-sdk/integrations/superoptix/server/python/tests/__init__.py b/typescript-sdk/integrations/superoptix/server/python/tests/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/typescript-sdk/integrations/superoptix/src/index.ts b/typescript-sdk/integrations/superoptix/src/index.ts new file mode 100644 index 000000000..21fc3a318 --- /dev/null +++ b/typescript-sdk/integrations/superoptix/src/index.ts @@ -0,0 +1,3 @@ +import { HttpAgent } from "@ag-ui/client"; + +export class SuperOptiXAgent extends HttpAgent {} diff --git a/typescript-sdk/integrations/superoptix/tsconfig.json b/typescript-sdk/integrations/superoptix/tsconfig.json new file mode 100644 index 000000000..d12ec063d --- /dev/null +++ b/typescript-sdk/integrations/superoptix/tsconfig.json @@ -0,0 +1,24 @@ +{ + "compilerOptions": { + "target": "es2017", + "module": "esnext", + "lib": ["dom", "dom.iterable", "esnext"], + "declaration": true, + "declarationMap": true, + "sourceMap": true, + "moduleResolution": "node", + "skipLibCheck": true, + "strict": true, + "jsx": "react-jsx", + "esModuleInterop": true, + "resolveJsonModule": true, + "isolatedModules": true, + "baseUrl": ".", + "paths": { + "@/*": ["./src/*"] + }, + "stripInternal": true + }, + "include": ["src"], + "exclude": ["node_modules", "dist"] +} diff --git a/typescript-sdk/integrations/superoptix/tsup.config.ts b/typescript-sdk/integrations/superoptix/tsup.config.ts new file mode 100644 index 000000000..12b69b8fb --- /dev/null +++ b/typescript-sdk/integrations/superoptix/tsup.config.ts @@ -0,0 +1,11 @@ +import { defineConfig } from "tsup"; + +export default defineConfig({ + entry: ["src/index.ts"], + format: ["cjs", "esm"], + dts: true, + splitting: false, + sourcemap: true, + clean: true, + minify: true, +}); diff --git a/typescript-sdk/pnpm-lock.yaml b/typescript-sdk/pnpm-lock.yaml index 239a3f75c..73438a99b 100644 --- a/typescript-sdk/pnpm-lock.yaml +++ b/typescript-sdk/pnpm-lock.yaml @@ -105,6 +105,9 @@ importers: '@ag-ui/server-starter-all-features': specifier: workspace:* version: link:../../integrations/server-starter-all-features + '@ag-ui/superoptix': + specifier: workspace:* + version: link:../../integrations/superoptix '@ag-ui/vercel-ai-sdk': specifier: workspace:* version: link:../../integrations/vercel-ai-sdk @@ -119,7 +122,7 @@ importers: version: 1.9.2(@types/react@19.1.5)(graphql@16.11.0)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) '@copilotkit/runtime': specifier: 1.9.2 - version: 1.9.2(@ag-ui/client@0.0.35)(@ag-ui/core@0.0.35)(@ag-ui/encoder@0.0.35)(@ag-ui/proto@0.0.35)(@aws-crypto/sha256-js@5.2.0)(@aws-sdk/client-bedrock-agent-runtime@3.844.0)(@aws-sdk/client-bedrock-runtime@3.844.0)(@aws-sdk/client-dynamodb@3.844.0)(@aws-sdk/client-kendra@3.844.0)(@aws-sdk/credential-provider-node@3.844.0)(@browserbasehq/sdk@2.6.0)(@browserbasehq/stagehand@2.4.0(deepmerge@4.3.1)(dotenv@17.0.1)(react@19.1.0)(zod@3.25.67))(@ibm-cloud/watsonx-ai@1.6.8)(@libsql/client@0.15.9)(@opentelemetry/api@1.9.0)(@opentelemetry/exporter-trace-otlp-proto@0.201.1(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@2.0.1(@opentelemetry/api@1.9.0))(@smithy/eventstream-codec@4.0.4)(@smithy/protocol-http@5.1.2)(@smithy/signature-v4@5.1.2)(@smithy/util-utf8@4.0.0)(@upstash/redis@1.35.1)(axios@1.10.0)(cohere-ai@7.17.1)(fast-xml-parser@5.2.5)(google-auth-library@10.1.0)(ibm-cloud-sdk-core@5.4.0)(ignore@7.0.4)(jsonwebtoken@9.0.2)(lodash@4.17.21)(pg@8.16.3)(playwright@1.53.2)(react@19.1.0)(redis@5.6.1)(ws@8.18.3) + version: 1.9.2(@ag-ui/client@0.0.35)(@ag-ui/core@0.0.35)(@ag-ui/encoder@0.0.35)(@ag-ui/proto@0.0.35)(@aws-crypto/sha256-js@5.2.0)(@aws-sdk/client-bedrock-agent-runtime@3.844.0)(@aws-sdk/client-bedrock-runtime@3.844.0)(@aws-sdk/client-dynamodb@3.844.0)(@aws-sdk/client-kendra@3.844.0)(@aws-sdk/credential-provider-node@3.844.0)(@browserbasehq/sdk@2.6.0)(@browserbasehq/stagehand@2.4.0(deepmerge@4.3.1)(dotenv@17.0.1)(react@19.1.0)(zod@3.25.67))(@ibm-cloud/watsonx-ai@1.6.8)(@opentelemetry/api@1.9.0)(@smithy/util-utf8@2.3.0)(@upstash/redis@1.35.1)(axios@1.10.0)(cohere-ai@7.17.1)(fast-xml-parser@5.2.5)(google-auth-library@10.1.0)(ibm-cloud-sdk-core@5.4.0)(ignore@5.3.2)(jsonwebtoken@9.0.2)(lodash@4.17.21)(pg@8.16.3)(playwright@1.53.2)(react@19.1.0)(redis@5.6.1)(ws@8.18.3) '@copilotkit/runtime-client-gql': specifier: 1.9.2 version: 1.9.2(graphql@16.11.0)(react@19.1.0) @@ -424,7 +427,7 @@ importers: version: 1.2.11(zod@3.25.67) '@copilotkit/runtime': specifier: ^1.8.13 - version: 1.8.13(@ag-ui/client@packages+client)(@ag-ui/core@0.0.35)(@ag-ui/encoder@0.0.35)(@ag-ui/proto@0.0.35)(@aws-crypto/sha256-js@5.2.0)(@aws-sdk/client-bedrock-agent-runtime@3.844.0)(@aws-sdk/client-bedrock-runtime@3.844.0)(@aws-sdk/client-dynamodb@3.844.0)(@aws-sdk/client-kendra@3.844.0)(@aws-sdk/credential-provider-node@3.844.0)(@browserbasehq/sdk@2.6.0)(@browserbasehq/stagehand@2.4.0(deepmerge@4.3.1)(dotenv@17.0.1)(react@19.1.0)(zod@3.25.67))(@ibm-cloud/watsonx-ai@1.6.8)(@langchain/aws@0.1.11(@langchain/core@0.3.66(@opentelemetry/api@1.9.0)(@opentelemetry/exporter-trace-otlp-proto@0.201.1(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@2.0.1(@opentelemetry/api@1.9.0))(openai@4.100.0(ws@8.18.3)(zod@3.25.71))))(@libsql/client@0.15.9)(@opentelemetry/api@1.9.0)(@opentelemetry/exporter-trace-otlp-proto@0.201.1(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@2.0.1(@opentelemetry/api@1.9.0))(@smithy/eventstream-codec@4.0.4)(@smithy/protocol-http@5.1.2)(@smithy/signature-v4@5.1.2)(@smithy/util-utf8@4.0.0)(@upstash/redis@1.35.1)(axios@1.10.0)(cohere-ai@7.17.1)(fast-xml-parser@5.2.5)(google-auth-library@10.1.0)(ibm-cloud-sdk-core@5.4.0)(ignore@7.0.4)(jsonwebtoken@9.0.2)(lodash@4.17.21)(pg@8.16.3)(playwright@1.53.2)(react@19.1.0)(redis@5.6.1)(ws@8.18.3) + version: 1.8.13(ba409de7c985babebe1439b905cb0871) '@mastra/client-js': specifier: ^0.10.9 version: 0.10.9(@sinclair/typebox@0.34.37)(openapi-types@12.1.3)(react@19.1.0)(zod@3.25.67) @@ -569,6 +572,34 @@ importers: specifier: ^5.3.3 version: 5.8.2 + integrations/superoptix: + dependencies: + '@ag-ui/client': + specifier: workspace:* + version: link:../../packages/client + rxjs: + specifier: 7.8.1 + version: 7.8.1 + devDependencies: + '@types/jest': + specifier: ^29.5.14 + version: 29.5.14 + '@types/node': + specifier: ^20.11.19 + version: 20.19.4 + jest: + specifier: ^29.7.0 + version: 29.7.0(@types/node@20.19.4) + ts-jest: + specifier: ^29.1.2 + version: 29.3.4(@babel/core@7.27.1)(@jest/transform@29.7.0)(@jest/types@29.6.3)(babel-jest@29.7.0(@babel/core@7.27.1))(jest@29.7.0(@types/node@20.19.4))(typescript@5.8.2) + tsup: + specifier: ^8.0.2 + version: 8.5.0(jiti@2.4.2)(postcss@8.5.6)(tsx@4.20.3)(typescript@5.8.2)(yaml@2.8.0) + typescript: + specifier: ^5.3.3 + version: 5.8.2 + integrations/vercel-ai-sdk: dependencies: '@ag-ui/client': @@ -700,10 +731,10 @@ importers: version: 29.5.14 jest: specifier: ^29.7.0 - version: 29.7.0(@types/node@20.19.4) + version: 29.7.0 ts-jest: specifier: ^29.1.2 - version: 29.3.4(@babel/core@7.27.1)(@jest/transform@29.7.0)(@jest/types@29.6.3)(babel-jest@29.7.0(@babel/core@7.27.1))(esbuild@0.25.4)(jest@29.7.0(@types/node@20.19.4))(typescript@5.8.2) + version: 29.3.4(@babel/core@7.27.1)(@jest/transform@29.7.0)(@jest/types@29.6.3)(babel-jest@29.7.0(@babel/core@7.27.1))(jest@29.7.0)(typescript@5.8.2) tsup: specifier: ^8.0.2 version: 8.5.0(jiti@2.4.2)(postcss@8.5.6)(tsx@4.20.3)(typescript@5.8.2)(yaml@2.8.0) @@ -725,10 +756,10 @@ importers: version: 29.5.14 jest: specifier: ^29.7.0 - version: 29.7.0(@types/node@20.19.4) + version: 29.7.0 ts-jest: specifier: ^29.1.2 - version: 29.3.4(@babel/core@7.27.1)(@jest/transform@29.7.0)(@jest/types@29.6.3)(babel-jest@29.7.0(@babel/core@7.27.1))(esbuild@0.25.4)(jest@29.7.0(@types/node@20.19.4))(typescript@5.8.2) + version: 29.3.4(@babel/core@7.27.1)(@jest/transform@29.7.0)(@jest/types@29.6.3)(babel-jest@29.7.0(@babel/core@7.27.1))(jest@29.7.0)(typescript@5.8.2) tsup: specifier: ^8.0.2 version: 8.5.0(jiti@2.4.2)(postcss@8.5.6)(tsx@4.20.3)(typescript@5.8.2)(yaml@2.8.0) @@ -756,10 +787,10 @@ importers: version: 29.5.14 jest: specifier: ^29.7.0 - version: 29.7.0(@types/node@20.19.4) + version: 29.7.0 ts-jest: specifier: ^29.1.2 - version: 29.3.4(@babel/core@7.27.1)(@jest/transform@29.7.0)(@jest/types@29.6.3)(babel-jest@29.7.0(@babel/core@7.27.1))(esbuild@0.25.4)(jest@29.7.0(@types/node@20.19.4))(typescript@5.8.2) + version: 29.3.4(@babel/core@7.27.1)(@jest/transform@29.7.0)(@jest/types@29.6.3)(babel-jest@29.7.0(@babel/core@7.27.1))(jest@29.7.0)(typescript@5.8.2) ts-proto: specifier: ^2.7.0 version: 2.7.0 @@ -7145,6 +7176,7 @@ packages: libsql@0.5.13: resolution: {integrity: sha512-5Bwoa/CqzgkTwySgqHA5TsaUDRrdLIbdM4egdPcaAnqO3aC+qAgS6BwdzuZwARA5digXwiskogZ8H7Yy4XfdOg==} + cpu: [x64, arm64, wasm32, arm] os: [darwin, linux, win32] lightningcss-darwin-arm64@1.30.1: @@ -9572,11 +9604,11 @@ snapshots: '@ag-ui/core': 0.0.35 '@ag-ui/proto': 0.0.35 - '@ag-ui/langgraph@0.0.6(@opentelemetry/api@1.9.0)(@opentelemetry/exporter-trace-otlp-proto@0.201.1(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@2.0.1(@opentelemetry/api@1.9.0))(openai@4.104.0(ws@8.18.3)(zod@3.25.71))(react@19.1.0)': + '@ag-ui/langgraph@0.0.6(@opentelemetry/api@1.9.0)(openai@4.104.0(ws@8.18.3)(zod@3.25.71))(react@19.1.0)': dependencies: '@ag-ui/client': 0.0.34 - '@langchain/core': 0.3.66(@opentelemetry/api@1.9.0)(@opentelemetry/exporter-trace-otlp-proto@0.201.1(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@2.0.1(@opentelemetry/api@1.9.0))(openai@4.104.0(ws@8.18.3)(zod@3.25.71)) - '@langchain/langgraph-sdk': 0.0.78(@langchain/core@0.3.66(@opentelemetry/api@1.9.0)(@opentelemetry/exporter-trace-otlp-proto@0.201.1(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@2.0.1(@opentelemetry/api@1.9.0))(openai@4.104.0(ws@8.18.3)(zod@3.25.71)))(react@19.1.0) + '@langchain/core': 0.3.66(@opentelemetry/api@1.9.0)(openai@4.100.0(ws@8.18.3)(zod@3.25.67)) + '@langchain/langgraph-sdk': 0.0.78(@langchain/core@0.3.66(@opentelemetry/api@1.9.0)(openai@4.104.0(ws@8.18.3)(zod@3.25.71)))(react@19.1.0) partial-json: 0.1.7 rxjs: 7.8.1 transitivePeerDependencies: @@ -11054,8 +11086,8 @@ snapshots: - encoding - graphql - ? '@copilotkit/runtime@1.8.13(@ag-ui/client@packages+client)(@ag-ui/core@0.0.35)(@ag-ui/encoder@0.0.35)(@ag-ui/proto@0.0.35)(@aws-crypto/sha256-js@5.2.0)(@aws-sdk/client-bedrock-agent-runtime@3.844.0)(@aws-sdk/client-bedrock-runtime@3.844.0)(@aws-sdk/client-dynamodb@3.844.0)(@aws-sdk/client-kendra@3.844.0)(@aws-sdk/credential-provider-node@3.844.0)(@browserbasehq/sdk@2.6.0)(@browserbasehq/stagehand@2.4.0(deepmerge@4.3.1)(dotenv@17.0.1)(react@19.1.0)(zod@3.25.67))(@ibm-cloud/watsonx-ai@1.6.8)(@langchain/aws@0.1.11(@langchain/core@0.3.66(@opentelemetry/api@1.9.0)(@opentelemetry/exporter-trace-otlp-proto@0.201.1(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@2.0.1(@opentelemetry/api@1.9.0))(openai@4.100.0(ws@8.18.3)(zod@3.25.71))))(@libsql/client@0.15.9)(@opentelemetry/api@1.9.0)(@opentelemetry/exporter-trace-otlp-proto@0.201.1(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@2.0.1(@opentelemetry/api@1.9.0))(@smithy/eventstream-codec@4.0.4)(@smithy/protocol-http@5.1.2)(@smithy/signature-v4@5.1.2)(@smithy/util-utf8@4.0.0)(@upstash/redis@1.35.1)(axios@1.10.0)(cohere-ai@7.17.1)(fast-xml-parser@5.2.5)(google-auth-library@10.1.0)(ibm-cloud-sdk-core@5.4.0)(ignore@7.0.4)(jsonwebtoken@9.0.2)(lodash@4.17.21)(pg@8.16.3)(playwright@1.53.2)(react@19.1.0)(redis@5.6.1)(ws@8.18.3)' - : dependencies: + '@copilotkit/runtime@1.8.13(ba409de7c985babebe1439b905cb0871)': + dependencies: '@ag-ui/client': link:packages/client '@ag-ui/core': 0.0.35 '@ag-ui/encoder': 0.0.35 @@ -11063,7 +11095,7 @@ snapshots: '@anthropic-ai/sdk': 0.27.3 '@copilotkit/shared': 1.8.13 '@graphql-yoga/plugin-defer-stream': 3.13.4(graphql-yoga@5.13.4(graphql@16.11.0))(graphql@16.11.0) - '@langchain/community': 0.3.43(@aws-crypto/sha256-js@5.2.0)(@aws-sdk/client-bedrock-agent-runtime@3.844.0)(@aws-sdk/client-bedrock-runtime@3.844.0)(@aws-sdk/client-dynamodb@3.844.0)(@aws-sdk/client-kendra@3.844.0)(@aws-sdk/credential-provider-node@3.844.0)(@browserbasehq/sdk@2.6.0)(@browserbasehq/stagehand@2.4.0(deepmerge@4.3.1)(dotenv@17.0.1)(react@19.1.0)(zod@3.25.67))(@ibm-cloud/watsonx-ai@1.6.8)(@langchain/aws@0.1.11(@langchain/core@0.3.66(@opentelemetry/api@1.9.0)(@opentelemetry/exporter-trace-otlp-proto@0.201.1(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@2.0.1(@opentelemetry/api@1.9.0))(openai@4.100.0(ws@8.18.3)(zod@3.25.71))))(@langchain/core@0.3.66(@opentelemetry/api@1.9.0)(@opentelemetry/exporter-trace-otlp-proto@0.201.1(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@2.0.1(@opentelemetry/api@1.9.0))(openai@4.100.0(ws@8.18.3)(zod@3.25.71)))(@libsql/client@0.15.9)(@smithy/eventstream-codec@4.0.4)(@smithy/protocol-http@5.1.2)(@smithy/signature-v4@5.1.2)(@smithy/util-utf8@4.0.0)(@upstash/redis@1.35.1)(axios@1.10.0)(cohere-ai@7.17.1)(fast-xml-parser@5.2.5)(google-auth-library@10.1.0)(ibm-cloud-sdk-core@5.4.0)(ignore@7.0.4)(jsonwebtoken@9.0.2)(lodash@4.17.21)(openai@4.100.0(ws@8.18.3)(zod@3.25.71))(pg@8.16.3)(playwright@1.53.2)(redis@5.6.1)(ws@8.18.3) + '@langchain/community': 0.3.43(311d9a4bf363ac96cce7364919fdf4e9) '@langchain/core': 0.3.66(@opentelemetry/api@1.9.0)(@opentelemetry/exporter-trace-otlp-proto@0.201.1(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@2.0.1(@opentelemetry/api@1.9.0))(openai@4.100.0(ws@8.18.3)(zod@3.25.71)) '@langchain/google-gauth': 0.1.8(@langchain/core@0.3.66(@opentelemetry/api@1.9.0)(@opentelemetry/exporter-trace-otlp-proto@0.201.1(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@2.0.1(@opentelemetry/api@1.9.0))(openai@4.100.0(ws@8.18.3)(zod@3.25.71)))(zod@3.25.71) '@langchain/langgraph-sdk': 0.0.70(@langchain/core@0.3.66(@opentelemetry/api@1.9.0)(@opentelemetry/exporter-trace-otlp-proto@0.201.1(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@2.0.1(@opentelemetry/api@1.9.0))(openai@4.100.0(ws@8.18.3)(zod@3.25.71)))(react@19.1.0) @@ -11234,22 +11266,22 @@ snapshots: - ws - youtubei.js - ? '@copilotkit/runtime@1.9.2(@ag-ui/client@0.0.35)(@ag-ui/core@0.0.35)(@ag-ui/encoder@0.0.35)(@ag-ui/proto@0.0.35)(@aws-crypto/sha256-js@5.2.0)(@aws-sdk/client-bedrock-agent-runtime@3.844.0)(@aws-sdk/client-bedrock-runtime@3.844.0)(@aws-sdk/client-dynamodb@3.844.0)(@aws-sdk/client-kendra@3.844.0)(@aws-sdk/credential-provider-node@3.844.0)(@browserbasehq/sdk@2.6.0)(@browserbasehq/stagehand@2.4.0(deepmerge@4.3.1)(dotenv@17.0.1)(react@19.1.0)(zod@3.25.67))(@ibm-cloud/watsonx-ai@1.6.8)(@libsql/client@0.15.9)(@opentelemetry/api@1.9.0)(@opentelemetry/exporter-trace-otlp-proto@0.201.1(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@2.0.1(@opentelemetry/api@1.9.0))(@smithy/eventstream-codec@4.0.4)(@smithy/protocol-http@5.1.2)(@smithy/signature-v4@5.1.2)(@smithy/util-utf8@4.0.0)(@upstash/redis@1.35.1)(axios@1.10.0)(cohere-ai@7.17.1)(fast-xml-parser@5.2.5)(google-auth-library@10.1.0)(ibm-cloud-sdk-core@5.4.0)(ignore@7.0.4)(jsonwebtoken@9.0.2)(lodash@4.17.21)(pg@8.16.3)(playwright@1.53.2)(react@19.1.0)(redis@5.6.1)(ws@8.18.3)' - : dependencies: + '@copilotkit/runtime@1.9.2(@ag-ui/client@0.0.35)(@ag-ui/core@0.0.35)(@ag-ui/encoder@0.0.35)(@ag-ui/proto@0.0.35)(@aws-crypto/sha256-js@5.2.0)(@aws-sdk/client-bedrock-agent-runtime@3.844.0)(@aws-sdk/client-bedrock-runtime@3.844.0)(@aws-sdk/client-dynamodb@3.844.0)(@aws-sdk/client-kendra@3.844.0)(@aws-sdk/credential-provider-node@3.844.0)(@browserbasehq/sdk@2.6.0)(@browserbasehq/stagehand@2.4.0(deepmerge@4.3.1)(dotenv@17.0.1)(react@19.1.0)(zod@3.25.67))(@ibm-cloud/watsonx-ai@1.6.8)(@opentelemetry/api@1.9.0)(@smithy/util-utf8@2.3.0)(@upstash/redis@1.35.1)(axios@1.10.0)(cohere-ai@7.17.1)(fast-xml-parser@5.2.5)(google-auth-library@10.1.0)(ibm-cloud-sdk-core@5.4.0)(ignore@5.3.2)(jsonwebtoken@9.0.2)(lodash@4.17.21)(pg@8.16.3)(playwright@1.53.2)(react@19.1.0)(redis@5.6.1)(ws@8.18.3)': + dependencies: '@ag-ui/client': 0.0.35 '@ag-ui/core': 0.0.35 '@ag-ui/encoder': 0.0.35 - '@ag-ui/langgraph': 0.0.6(@opentelemetry/api@1.9.0)(@opentelemetry/exporter-trace-otlp-proto@0.201.1(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@2.0.1(@opentelemetry/api@1.9.0))(openai@4.104.0(ws@8.18.3)(zod@3.25.71))(react@19.1.0) + '@ag-ui/langgraph': 0.0.6(@opentelemetry/api@1.9.0)(openai@4.104.0(ws@8.18.3)(zod@3.25.71))(react@19.1.0) '@ag-ui/proto': 0.0.35 '@anthropic-ai/sdk': 0.27.3 '@copilotkit/shared': 1.9.2 '@graphql-yoga/plugin-defer-stream': 3.13.4(graphql-yoga@5.13.4(graphql@16.11.0))(graphql@16.11.0) - '@langchain/aws': 0.1.11(@langchain/core@0.3.66(@opentelemetry/api@1.9.0)(@opentelemetry/exporter-trace-otlp-proto@0.201.1(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@2.0.1(@opentelemetry/api@1.9.0))(openai@4.100.0(ws@8.18.3)(zod@3.25.71))) - '@langchain/community': 0.3.43(@aws-crypto/sha256-js@5.2.0)(@aws-sdk/client-bedrock-agent-runtime@3.844.0)(@aws-sdk/client-bedrock-runtime@3.844.0)(@aws-sdk/client-dynamodb@3.844.0)(@aws-sdk/client-kendra@3.844.0)(@aws-sdk/credential-provider-node@3.844.0)(@browserbasehq/sdk@2.6.0)(@browserbasehq/stagehand@2.4.0(deepmerge@4.3.1)(dotenv@17.0.1)(react@19.1.0)(zod@3.25.67))(@ibm-cloud/watsonx-ai@1.6.8)(@langchain/aws@0.1.11(@langchain/core@0.3.66(@opentelemetry/api@1.9.0)(@opentelemetry/exporter-trace-otlp-proto@0.201.1(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@2.0.1(@opentelemetry/api@1.9.0))(openai@4.104.0(ws@8.18.3)(zod@3.25.71))))(@langchain/core@0.3.66(@opentelemetry/api@1.9.0)(@opentelemetry/exporter-trace-otlp-proto@0.201.1(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@2.0.1(@opentelemetry/api@1.9.0))(openai@4.104.0(ws@8.18.3)(zod@3.25.71)))(@libsql/client@0.15.9)(@smithy/eventstream-codec@4.0.4)(@smithy/protocol-http@5.1.2)(@smithy/signature-v4@5.1.2)(@smithy/util-utf8@4.0.0)(@upstash/redis@1.35.1)(axios@1.10.0)(cohere-ai@7.17.1)(fast-xml-parser@5.2.5)(google-auth-library@10.1.0)(ibm-cloud-sdk-core@5.4.0)(ignore@7.0.4)(jsonwebtoken@9.0.2)(lodash@4.17.21)(openai@4.104.0(ws@8.18.3)(zod@3.25.71))(pg@8.16.3)(playwright@1.53.2)(redis@5.6.1)(ws@8.18.3) - '@langchain/core': 0.3.66(@opentelemetry/api@1.9.0)(@opentelemetry/exporter-trace-otlp-proto@0.201.1(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@2.0.1(@opentelemetry/api@1.9.0))(openai@4.100.0(ws@8.18.3)(zod@3.25.71)) - '@langchain/google-gauth': 0.1.8(@langchain/core@0.3.66(@opentelemetry/api@1.9.0)(@opentelemetry/exporter-trace-otlp-proto@0.201.1(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@2.0.1(@opentelemetry/api@1.9.0))(openai@4.100.0(ws@8.18.3)(zod@3.25.71)))(zod@3.25.71) - '@langchain/langgraph-sdk': 0.0.70(@langchain/core@0.3.66(@opentelemetry/api@1.9.0)(@opentelemetry/exporter-trace-otlp-proto@0.201.1(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@2.0.1(@opentelemetry/api@1.9.0))(openai@4.100.0(ws@8.18.3)(zod@3.25.71)))(react@19.1.0) - '@langchain/openai': 0.4.9(@langchain/core@0.3.66(@opentelemetry/api@1.9.0)(@opentelemetry/exporter-trace-otlp-proto@0.201.1(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@2.0.1(@opentelemetry/api@1.9.0))(openai@4.100.0(ws@8.18.3)(zod@3.25.71)))(ws@8.18.3) + '@langchain/aws': 0.1.11(@langchain/core@0.3.66(@opentelemetry/api@1.9.0)(openai@4.104.0(ws@8.18.3)(zod@3.25.71))) + '@langchain/community': 0.3.43(@aws-crypto/sha256-js@5.2.0)(@aws-sdk/client-bedrock-agent-runtime@3.844.0)(@aws-sdk/client-bedrock-runtime@3.844.0)(@aws-sdk/client-dynamodb@3.844.0)(@aws-sdk/client-kendra@3.844.0)(@aws-sdk/credential-provider-node@3.844.0)(@browserbasehq/sdk@2.6.0)(@browserbasehq/stagehand@2.4.0(deepmerge@4.3.1)(dotenv@17.0.1)(react@19.1.0)(zod@3.25.67))(@ibm-cloud/watsonx-ai@1.6.8)(@langchain/aws@0.1.11(@langchain/core@0.3.66(@opentelemetry/api@1.9.0)(openai@4.104.0(ws@8.18.3)(zod@3.25.71))))(@langchain/core@0.3.66(@opentelemetry/api@1.9.0)(openai@4.104.0(ws@8.18.3)(zod@3.25.71)))(@smithy/util-utf8@2.3.0)(@upstash/redis@1.35.1)(axios@1.10.0)(cohere-ai@7.17.1)(fast-xml-parser@5.2.5)(google-auth-library@10.1.0)(ibm-cloud-sdk-core@5.4.0)(ignore@5.3.2)(jsonwebtoken@9.0.2)(lodash@4.17.21)(openai@4.104.0(ws@8.18.3)(zod@3.25.71))(pg@8.16.3)(playwright@1.53.2)(redis@5.6.1)(ws@8.18.3) + '@langchain/core': 0.3.66(@opentelemetry/api@1.9.0)(openai@4.100.0(ws@8.18.3)(zod@3.25.67)) + '@langchain/google-gauth': 0.1.8(@langchain/core@0.3.66(@opentelemetry/api@1.9.0)(openai@4.104.0(ws@8.18.3)(zod@3.25.71)))(zod@3.25.71) + '@langchain/langgraph-sdk': 0.0.70(@langchain/core@0.3.66(@opentelemetry/api@1.9.0)(openai@4.104.0(ws@8.18.3)(zod@3.25.71)))(react@19.1.0) + '@langchain/openai': 0.4.9(@langchain/core@0.3.66(@opentelemetry/api@1.9.0)(openai@4.104.0(ws@8.18.3)(zod@3.25.71)))(ws@8.18.3) class-transformer: 0.5.1 class-validator: 0.14.2 express: 4.21.2 @@ -11257,7 +11289,7 @@ snapshots: graphql-scalars: 1.24.2(graphql@16.11.0) graphql-yoga: 5.13.4(graphql@16.11.0) groq-sdk: 0.5.0 - langchain: 0.3.26(@langchain/aws@0.1.11(@langchain/core@0.3.66(@opentelemetry/api@1.9.0)(@opentelemetry/exporter-trace-otlp-proto@0.201.1(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@2.0.1(@opentelemetry/api@1.9.0))(openai@4.104.0(ws@8.18.3)(zod@3.25.71))))(@langchain/core@0.3.66(@opentelemetry/api@1.9.0)(@opentelemetry/exporter-trace-otlp-proto@0.201.1(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@2.0.1(@opentelemetry/api@1.9.0))(openai@4.104.0(ws@8.18.3)(zod@3.25.71)))(axios@1.10.0)(openai@4.104.0(ws@8.18.3)(zod@3.25.71))(ws@8.18.3) + langchain: 0.3.26(@langchain/aws@0.1.11(@langchain/core@0.3.66(@opentelemetry/api@1.9.0)(openai@4.104.0(ws@8.18.3)(zod@3.25.71))))(@langchain/core@0.3.66(@opentelemetry/api@1.9.0)(openai@4.104.0(ws@8.18.3)(zod@3.25.71)))(axios@1.10.0)(openai@4.104.0(ws@8.18.3)(zod@3.25.71))(ws@8.18.3) openai: 4.104.0(ws@8.18.3)(zod@3.25.71) partial-json: 0.1.7 pino: 9.7.0 @@ -12230,9 +12262,20 @@ snapshots: '@langchain/core': 0.3.66(@opentelemetry/api@1.9.0)(@opentelemetry/exporter-trace-otlp-proto@0.201.1(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@2.0.1(@opentelemetry/api@1.9.0))(openai@4.100.0(ws@8.18.3)(zod@3.25.71)) transitivePeerDependencies: - aws-crt + optional: true + + '@langchain/aws@0.1.11(@langchain/core@0.3.66(@opentelemetry/api@1.9.0)(openai@4.104.0(ws@8.18.3)(zod@3.25.71)))': + dependencies: + '@aws-sdk/client-bedrock-agent-runtime': 3.844.0 + '@aws-sdk/client-bedrock-runtime': 3.844.0 + '@aws-sdk/client-kendra': 3.844.0 + '@aws-sdk/credential-provider-node': 3.844.0 + '@langchain/core': 0.3.66(@opentelemetry/api@1.9.0)(openai@4.100.0(ws@8.18.3)(zod@3.25.67)) + transitivePeerDependencies: + - aws-crt - ? '@langchain/community@0.3.43(@aws-crypto/sha256-js@5.2.0)(@aws-sdk/client-bedrock-agent-runtime@3.844.0)(@aws-sdk/client-bedrock-runtime@3.844.0)(@aws-sdk/client-dynamodb@3.844.0)(@aws-sdk/client-kendra@3.844.0)(@aws-sdk/credential-provider-node@3.844.0)(@browserbasehq/sdk@2.6.0)(@browserbasehq/stagehand@2.4.0(deepmerge@4.3.1)(dotenv@17.0.1)(react@19.1.0)(zod@3.25.67))(@ibm-cloud/watsonx-ai@1.6.8)(@langchain/aws@0.1.11(@langchain/core@0.3.66(@opentelemetry/api@1.9.0)(@opentelemetry/exporter-trace-otlp-proto@0.201.1(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@2.0.1(@opentelemetry/api@1.9.0))(openai@4.100.0(ws@8.18.3)(zod@3.25.71))))(@langchain/core@0.3.66(@opentelemetry/api@1.9.0)(@opentelemetry/exporter-trace-otlp-proto@0.201.1(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@2.0.1(@opentelemetry/api@1.9.0))(openai@4.100.0(ws@8.18.3)(zod@3.25.71)))(@libsql/client@0.15.9)(@smithy/eventstream-codec@4.0.4)(@smithy/protocol-http@5.1.2)(@smithy/signature-v4@5.1.2)(@smithy/util-utf8@4.0.0)(@upstash/redis@1.35.1)(axios@1.10.0)(cohere-ai@7.17.1)(fast-xml-parser@5.2.5)(google-auth-library@10.1.0)(ibm-cloud-sdk-core@5.4.0)(ignore@7.0.4)(jsonwebtoken@9.0.2)(lodash@4.17.21)(openai@4.100.0(ws@8.18.3)(zod@3.25.71))(pg@8.16.3)(playwright@1.53.2)(redis@5.6.1)(ws@8.18.3)' - : dependencies: + '@langchain/community@0.3.43(311d9a4bf363ac96cce7364919fdf4e9)': + dependencies: '@browserbasehq/stagehand': 2.4.0(deepmerge@4.3.1)(dotenv@17.0.1)(react@19.1.0)(zod@3.25.67) '@ibm-cloud/watsonx-ai': 1.6.8 '@langchain/core': 0.3.66(@opentelemetry/api@1.9.0)(@opentelemetry/exporter-trace-otlp-proto@0.201.1(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@2.0.1(@opentelemetry/api@1.9.0))(openai@4.100.0(ws@8.18.3)(zod@3.25.71)) @@ -12256,16 +12299,12 @@ snapshots: '@aws-sdk/client-kendra': 3.844.0 '@aws-sdk/credential-provider-node': 3.844.0 '@browserbasehq/sdk': 2.6.0 - '@libsql/client': 0.15.9 - '@smithy/eventstream-codec': 4.0.4 - '@smithy/protocol-http': 5.1.2 - '@smithy/signature-v4': 5.1.2 - '@smithy/util-utf8': 4.0.0 + '@smithy/util-utf8': 2.3.0 '@upstash/redis': 1.35.1 cohere-ai: 7.17.1 fast-xml-parser: 5.2.5 google-auth-library: 10.1.0 - ignore: 7.0.4 + ignore: 5.3.2 jsonwebtoken: 9.0.2 lodash: 4.17.21 pg: 8.16.3 @@ -12290,18 +12329,18 @@ snapshots: - handlebars - peggy - ? '@langchain/community@0.3.43(@aws-crypto/sha256-js@5.2.0)(@aws-sdk/client-bedrock-agent-runtime@3.844.0)(@aws-sdk/client-bedrock-runtime@3.844.0)(@aws-sdk/client-dynamodb@3.844.0)(@aws-sdk/client-kendra@3.844.0)(@aws-sdk/credential-provider-node@3.844.0)(@browserbasehq/sdk@2.6.0)(@browserbasehq/stagehand@2.4.0(deepmerge@4.3.1)(dotenv@17.0.1)(react@19.1.0)(zod@3.25.67))(@ibm-cloud/watsonx-ai@1.6.8)(@langchain/aws@0.1.11(@langchain/core@0.3.66(@opentelemetry/api@1.9.0)(@opentelemetry/exporter-trace-otlp-proto@0.201.1(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@2.0.1(@opentelemetry/api@1.9.0))(openai@4.104.0(ws@8.18.3)(zod@3.25.71))))(@langchain/core@0.3.66(@opentelemetry/api@1.9.0)(@opentelemetry/exporter-trace-otlp-proto@0.201.1(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@2.0.1(@opentelemetry/api@1.9.0))(openai@4.104.0(ws@8.18.3)(zod@3.25.71)))(@libsql/client@0.15.9)(@smithy/eventstream-codec@4.0.4)(@smithy/protocol-http@5.1.2)(@smithy/signature-v4@5.1.2)(@smithy/util-utf8@4.0.0)(@upstash/redis@1.35.1)(axios@1.10.0)(cohere-ai@7.17.1)(fast-xml-parser@5.2.5)(google-auth-library@10.1.0)(ibm-cloud-sdk-core@5.4.0)(ignore@7.0.4)(jsonwebtoken@9.0.2)(lodash@4.17.21)(openai@4.104.0(ws@8.18.3)(zod@3.25.71))(pg@8.16.3)(playwright@1.53.2)(redis@5.6.1)(ws@8.18.3)' - : dependencies: + '@langchain/community@0.3.43(@aws-crypto/sha256-js@5.2.0)(@aws-sdk/client-bedrock-agent-runtime@3.844.0)(@aws-sdk/client-bedrock-runtime@3.844.0)(@aws-sdk/client-dynamodb@3.844.0)(@aws-sdk/client-kendra@3.844.0)(@aws-sdk/credential-provider-node@3.844.0)(@browserbasehq/sdk@2.6.0)(@browserbasehq/stagehand@2.4.0(deepmerge@4.3.1)(dotenv@17.0.1)(react@19.1.0)(zod@3.25.67))(@ibm-cloud/watsonx-ai@1.6.8)(@langchain/aws@0.1.11(@langchain/core@0.3.66(@opentelemetry/api@1.9.0)(openai@4.104.0(ws@8.18.3)(zod@3.25.71))))(@langchain/core@0.3.66(@opentelemetry/api@1.9.0)(openai@4.104.0(ws@8.18.3)(zod@3.25.71)))(@smithy/util-utf8@2.3.0)(@upstash/redis@1.35.1)(axios@1.10.0)(cohere-ai@7.17.1)(fast-xml-parser@5.2.5)(google-auth-library@10.1.0)(ibm-cloud-sdk-core@5.4.0)(ignore@5.3.2)(jsonwebtoken@9.0.2)(lodash@4.17.21)(openai@4.104.0(ws@8.18.3)(zod@3.25.71))(pg@8.16.3)(playwright@1.53.2)(redis@5.6.1)(ws@8.18.3)': + dependencies: '@browserbasehq/stagehand': 2.4.0(deepmerge@4.3.1)(dotenv@17.0.1)(react@19.1.0)(zod@3.25.67) '@ibm-cloud/watsonx-ai': 1.6.8 - '@langchain/core': 0.3.66(@opentelemetry/api@1.9.0)(@opentelemetry/exporter-trace-otlp-proto@0.201.1(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@2.0.1(@opentelemetry/api@1.9.0))(openai@4.100.0(ws@8.18.3)(zod@3.25.71)) - '@langchain/openai': 0.4.9(@langchain/core@0.3.66(@opentelemetry/api@1.9.0)(@opentelemetry/exporter-trace-otlp-proto@0.201.1(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@2.0.1(@opentelemetry/api@1.9.0))(openai@4.100.0(ws@8.18.3)(zod@3.25.71)))(ws@8.18.3) + '@langchain/core': 0.3.66(@opentelemetry/api@1.9.0)(openai@4.100.0(ws@8.18.3)(zod@3.25.67)) + '@langchain/openai': 0.4.9(@langchain/core@0.3.66(@opentelemetry/api@1.9.0)(openai@4.104.0(ws@8.18.3)(zod@3.25.71)))(ws@8.18.3) binary-extensions: 2.3.0 expr-eval: 2.0.2 flat: 5.0.2 ibm-cloud-sdk-core: 5.4.0 js-yaml: 4.1.0 - langchain: 0.3.26(@langchain/aws@0.1.11(@langchain/core@0.3.66(@opentelemetry/api@1.9.0)(@opentelemetry/exporter-trace-otlp-proto@0.201.1(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@2.0.1(@opentelemetry/api@1.9.0))(openai@4.104.0(ws@8.18.3)(zod@3.25.71))))(@langchain/core@0.3.66(@opentelemetry/api@1.9.0)(@opentelemetry/exporter-trace-otlp-proto@0.201.1(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@2.0.1(@opentelemetry/api@1.9.0))(openai@4.104.0(ws@8.18.3)(zod@3.25.71)))(axios@1.10.0)(openai@4.104.0(ws@8.18.3)(zod@3.25.71))(ws@8.18.3) + langchain: 0.3.26(@langchain/aws@0.1.11(@langchain/core@0.3.66(@opentelemetry/api@1.9.0)(openai@4.104.0(ws@8.18.3)(zod@3.25.71))))(@langchain/core@0.3.66(@opentelemetry/api@1.9.0)(openai@4.104.0(ws@8.18.3)(zod@3.25.71)))(axios@1.10.0)(openai@4.104.0(ws@8.18.3)(zod@3.25.71))(ws@8.18.3) langsmith: 0.3.29(openai@4.104.0(ws@8.18.3)(zod@3.25.71)) openai: 4.104.0(ws@8.18.3)(zod@3.25.71) uuid: 10.0.0 @@ -12315,16 +12354,12 @@ snapshots: '@aws-sdk/client-kendra': 3.844.0 '@aws-sdk/credential-provider-node': 3.844.0 '@browserbasehq/sdk': 2.6.0 - '@libsql/client': 0.15.9 - '@smithy/eventstream-codec': 4.0.4 - '@smithy/protocol-http': 5.1.2 - '@smithy/signature-v4': 5.1.2 - '@smithy/util-utf8': 4.0.0 + '@smithy/util-utf8': 2.3.0 '@upstash/redis': 1.35.1 cohere-ai: 7.17.1 fast-xml-parser: 5.2.5 google-auth-library: 10.1.0 - ignore: 7.0.4 + ignore: 5.3.2 jsonwebtoken: 9.0.2 lodash: 4.17.21 pg: 8.16.3 @@ -12389,6 +12424,26 @@ snapshots: - '@opentelemetry/sdk-trace-base' - openai + '@langchain/core@0.3.66(@opentelemetry/api@1.9.0)(openai@4.100.0(ws@8.18.3)(zod@3.25.67))': + dependencies: + '@cfworker/json-schema': 4.1.1 + ansi-styles: 5.2.0 + camelcase: 6.3.0 + decamelize: 1.2.0 + js-tiktoken: 1.0.20 + langsmith: 0.3.49(@opentelemetry/api@1.9.0)(@opentelemetry/exporter-trace-otlp-proto@0.201.1(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@2.0.1(@opentelemetry/api@1.9.0))(openai@4.100.0(ws@8.18.3)(zod@3.25.71)) + mustache: 4.2.0 + p-queue: 6.6.2 + p-retry: 4.6.2 + uuid: 10.0.0 + zod: 3.25.71 + zod-to-json-schema: 3.24.6(zod@3.25.71) + transitivePeerDependencies: + - '@opentelemetry/api' + - '@opentelemetry/exporter-trace-otlp-proto' + - '@opentelemetry/sdk-trace-base' + - openai + '@langchain/google-common@0.1.8(@langchain/core@0.3.66(@opentelemetry/api@1.9.0)(@opentelemetry/exporter-trace-otlp-proto@0.201.1(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@2.0.1(@opentelemetry/api@1.9.0))(openai@4.100.0(ws@8.18.3)(zod@3.25.71)))(zod@3.25.71)': dependencies: '@langchain/core': 0.3.66(@opentelemetry/api@1.9.0)(@opentelemetry/exporter-trace-otlp-proto@0.201.1(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@2.0.1(@opentelemetry/api@1.9.0))(openai@4.100.0(ws@8.18.3)(zod@3.25.71)) @@ -12397,6 +12452,14 @@ snapshots: transitivePeerDependencies: - zod + '@langchain/google-common@0.1.8(@langchain/core@0.3.66(@opentelemetry/api@1.9.0)(openai@4.104.0(ws@8.18.3)(zod@3.25.71)))(zod@3.25.71)': + dependencies: + '@langchain/core': 0.3.66(@opentelemetry/api@1.9.0)(openai@4.100.0(ws@8.18.3)(zod@3.25.67)) + uuid: 10.0.0 + zod-to-json-schema: 3.24.6(zod@3.25.71) + transitivePeerDependencies: + - zod + '@langchain/google-gauth@0.1.8(@langchain/core@0.3.66(@opentelemetry/api@1.9.0)(@opentelemetry/exporter-trace-otlp-proto@0.201.1(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@2.0.1(@opentelemetry/api@1.9.0))(openai@4.100.0(ws@8.18.3)(zod@3.25.71)))(zod@3.25.71)': dependencies: '@langchain/core': 0.3.66(@opentelemetry/api@1.9.0)(@opentelemetry/exporter-trace-otlp-proto@0.201.1(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@2.0.1(@opentelemetry/api@1.9.0))(openai@4.100.0(ws@8.18.3)(zod@3.25.71)) @@ -12407,6 +12470,16 @@ snapshots: - supports-color - zod + '@langchain/google-gauth@0.1.8(@langchain/core@0.3.66(@opentelemetry/api@1.9.0)(openai@4.104.0(ws@8.18.3)(zod@3.25.71)))(zod@3.25.71)': + dependencies: + '@langchain/core': 0.3.66(@opentelemetry/api@1.9.0)(openai@4.100.0(ws@8.18.3)(zod@3.25.67)) + '@langchain/google-common': 0.1.8(@langchain/core@0.3.66(@opentelemetry/api@1.9.0)(openai@4.104.0(ws@8.18.3)(zod@3.25.71)))(zod@3.25.71) + google-auth-library: 8.9.0 + transitivePeerDependencies: + - encoding + - supports-color + - zod + '@langchain/langgraph-sdk@0.0.105(@langchain/core@0.3.66(@opentelemetry/api@1.9.0)(@opentelemetry/exporter-trace-otlp-proto@0.201.1(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@2.0.1(@opentelemetry/api@1.9.0))(openai@4.104.0(ws@8.18.3)(zod@3.25.71)))(react-dom@19.1.0(react@19.1.0))(react@19.1.0)': dependencies: '@types/json-schema': 7.0.15 @@ -12428,14 +12501,24 @@ snapshots: '@langchain/core': 0.3.66(@opentelemetry/api@1.9.0)(@opentelemetry/exporter-trace-otlp-proto@0.201.1(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@2.0.1(@opentelemetry/api@1.9.0))(openai@4.100.0(ws@8.18.3)(zod@3.25.71)) react: 19.1.0 - '@langchain/langgraph-sdk@0.0.78(@langchain/core@0.3.66(@opentelemetry/api@1.9.0)(@opentelemetry/exporter-trace-otlp-proto@0.201.1(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@2.0.1(@opentelemetry/api@1.9.0))(openai@4.104.0(ws@8.18.3)(zod@3.25.71)))(react@19.1.0)': + '@langchain/langgraph-sdk@0.0.70(@langchain/core@0.3.66(@opentelemetry/api@1.9.0)(openai@4.104.0(ws@8.18.3)(zod@3.25.71)))(react@19.1.0)': dependencies: '@types/json-schema': 7.0.15 p-queue: 6.6.2 p-retry: 4.6.2 uuid: 9.0.1 optionalDependencies: - '@langchain/core': 0.3.66(@opentelemetry/api@1.9.0)(@opentelemetry/exporter-trace-otlp-proto@0.201.1(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@2.0.1(@opentelemetry/api@1.9.0))(openai@4.104.0(ws@8.18.3)(zod@3.25.71)) + '@langchain/core': 0.3.66(@opentelemetry/api@1.9.0)(openai@4.100.0(ws@8.18.3)(zod@3.25.67)) + react: 19.1.0 + + '@langchain/langgraph-sdk@0.0.78(@langchain/core@0.3.66(@opentelemetry/api@1.9.0)(openai@4.104.0(ws@8.18.3)(zod@3.25.71)))(react@19.1.0)': + dependencies: + '@types/json-schema': 7.0.15 + p-queue: 6.6.2 + p-retry: 4.6.2 + uuid: 9.0.1 + optionalDependencies: + '@langchain/core': 0.3.66(@opentelemetry/api@1.9.0)(openai@4.100.0(ws@8.18.3)(zod@3.25.67)) react: 19.1.0 '@langchain/openai@0.4.9(@langchain/core@0.3.66(@opentelemetry/api@1.9.0)(@opentelemetry/exporter-trace-otlp-proto@0.201.1(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@2.0.1(@opentelemetry/api@1.9.0))(openai@4.100.0(ws@8.18.3)(zod@3.25.71)))(ws@8.18.3)': @@ -12449,11 +12532,27 @@ snapshots: - encoding - ws + '@langchain/openai@0.4.9(@langchain/core@0.3.66(@opentelemetry/api@1.9.0)(openai@4.104.0(ws@8.18.3)(zod@3.25.71)))(ws@8.18.3)': + dependencies: + '@langchain/core': 0.3.66(@opentelemetry/api@1.9.0)(openai@4.100.0(ws@8.18.3)(zod@3.25.67)) + js-tiktoken: 1.0.20 + openai: 4.104.0(ws@8.18.3)(zod@3.25.71) + zod: 3.25.71 + zod-to-json-schema: 3.24.6(zod@3.25.71) + transitivePeerDependencies: + - encoding + - ws + '@langchain/textsplitters@0.1.0(@langchain/core@0.3.66(@opentelemetry/api@1.9.0)(@opentelemetry/exporter-trace-otlp-proto@0.201.1(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@2.0.1(@opentelemetry/api@1.9.0))(openai@4.100.0(ws@8.18.3)(zod@3.25.71)))': dependencies: '@langchain/core': 0.3.66(@opentelemetry/api@1.9.0)(@opentelemetry/exporter-trace-otlp-proto@0.201.1(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@2.0.1(@opentelemetry/api@1.9.0))(openai@4.100.0(ws@8.18.3)(zod@3.25.71)) js-tiktoken: 1.0.20 + '@langchain/textsplitters@0.1.0(@langchain/core@0.3.66(@opentelemetry/api@1.9.0)(openai@4.104.0(ws@8.18.3)(zod@3.25.71)))': + dependencies: + '@langchain/core': 0.3.66(@opentelemetry/api@1.9.0)(openai@4.100.0(ws@8.18.3)(zod@3.25.67)) + js-tiktoken: 1.0.20 + '@libsql/client@0.15.9': dependencies: '@libsql/core': 0.15.9 @@ -16409,7 +16508,7 @@ snapshots: transitivePeerDependencies: - supports-color - eslint-module-utils@2.12.0(@typescript-eslint/parser@8.32.1(eslint@9.27.0(jiti@2.4.2))(typescript@5.8.2))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.10.1(eslint-plugin-import@2.31.0)(eslint@9.27.0(jiti@2.4.2)))(eslint@9.27.0(jiti@2.4.2)): + eslint-module-utils@2.12.0(@typescript-eslint/parser@8.32.1(eslint@9.27.0(jiti@2.4.2))(typescript@5.8.2))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.10.1)(eslint@9.27.0(jiti@2.4.2)): dependencies: debug: 3.2.7 optionalDependencies: @@ -16431,7 +16530,7 @@ snapshots: doctrine: 2.1.0 eslint: 9.27.0(jiti@2.4.2) eslint-import-resolver-node: 0.3.9 - eslint-module-utils: 2.12.0(@typescript-eslint/parser@8.32.1(eslint@9.27.0(jiti@2.4.2))(typescript@5.8.2))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.10.1(eslint-plugin-import@2.31.0)(eslint@9.27.0(jiti@2.4.2)))(eslint@9.27.0(jiti@2.4.2)) + eslint-module-utils: 2.12.0(@typescript-eslint/parser@8.32.1(eslint@9.27.0(jiti@2.4.2))(typescript@5.8.2))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.10.1)(eslint@9.27.0(jiti@2.4.2)) hasown: 2.0.2 is-core-module: 2.16.1 is-glob: 4.0.3 @@ -17375,7 +17474,7 @@ snapshots: isstream: 0.1.2 jsonwebtoken: 9.0.2 mime-types: 2.1.35 - retry-axios: 2.6.0(axios@1.10.0(debug@4.4.1)) + retry-axios: 2.6.0(axios@1.10.0) tough-cookie: 4.1.4 transitivePeerDependencies: - supports-color @@ -17703,6 +17802,25 @@ snapshots: - babel-plugin-macros - supports-color + jest-cli@29.7.0: + dependencies: + '@jest/core': 29.7.0 + '@jest/test-result': 29.7.0 + '@jest/types': 29.6.3 + chalk: 4.1.2 + create-jest: 29.7.0(@types/node@20.17.50) + exit: 0.1.2 + import-local: 3.2.0 + jest-config: 29.7.0(@types/node@20.17.50) + jest-util: 29.7.0 + jest-validate: 29.7.0 + yargs: 17.7.2 + transitivePeerDependencies: + - '@types/node' + - babel-plugin-macros + - supports-color + - ts-node + jest-cli@29.7.0(@types/node@20.17.50): dependencies: '@jest/core': 29.7.0 @@ -18023,6 +18141,18 @@ snapshots: merge-stream: 2.0.0 supports-color: 8.1.1 + jest@29.7.0: + dependencies: + '@jest/core': 29.7.0 + '@jest/types': 29.6.3 + import-local: 3.2.0 + jest-cli: 29.7.0 + transitivePeerDependencies: + - '@types/node' + - babel-plugin-macros + - supports-color + - ts-node + jest@29.7.0(@types/node@20.17.50): dependencies: '@jest/core': 29.7.0 @@ -18197,11 +18327,11 @@ snapshots: - openai - ws - langchain@0.3.26(@langchain/aws@0.1.11(@langchain/core@0.3.66(@opentelemetry/api@1.9.0)(@opentelemetry/exporter-trace-otlp-proto@0.201.1(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@2.0.1(@opentelemetry/api@1.9.0))(openai@4.104.0(ws@8.18.3)(zod@3.25.71))))(@langchain/core@0.3.66(@opentelemetry/api@1.9.0)(@opentelemetry/exporter-trace-otlp-proto@0.201.1(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@2.0.1(@opentelemetry/api@1.9.0))(openai@4.104.0(ws@8.18.3)(zod@3.25.71)))(axios@1.10.0)(openai@4.104.0(ws@8.18.3)(zod@3.25.71))(ws@8.18.3): + langchain@0.3.26(@langchain/aws@0.1.11(@langchain/core@0.3.66(@opentelemetry/api@1.9.0)(openai@4.104.0(ws@8.18.3)(zod@3.25.71))))(@langchain/core@0.3.66(@opentelemetry/api@1.9.0)(openai@4.104.0(ws@8.18.3)(zod@3.25.71)))(axios@1.10.0)(openai@4.104.0(ws@8.18.3)(zod@3.25.71))(ws@8.18.3): dependencies: - '@langchain/core': 0.3.66(@opentelemetry/api@1.9.0)(@opentelemetry/exporter-trace-otlp-proto@0.201.1(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@2.0.1(@opentelemetry/api@1.9.0))(openai@4.100.0(ws@8.18.3)(zod@3.25.71)) - '@langchain/openai': 0.4.9(@langchain/core@0.3.66(@opentelemetry/api@1.9.0)(@opentelemetry/exporter-trace-otlp-proto@0.201.1(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@2.0.1(@opentelemetry/api@1.9.0))(openai@4.100.0(ws@8.18.3)(zod@3.25.71)))(ws@8.18.3) - '@langchain/textsplitters': 0.1.0(@langchain/core@0.3.66(@opentelemetry/api@1.9.0)(@opentelemetry/exporter-trace-otlp-proto@0.201.1(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@2.0.1(@opentelemetry/api@1.9.0))(openai@4.100.0(ws@8.18.3)(zod@3.25.71))) + '@langchain/core': 0.3.66(@opentelemetry/api@1.9.0)(openai@4.100.0(ws@8.18.3)(zod@3.25.67)) + '@langchain/openai': 0.4.9(@langchain/core@0.3.66(@opentelemetry/api@1.9.0)(openai@4.104.0(ws@8.18.3)(zod@3.25.71)))(ws@8.18.3) + '@langchain/textsplitters': 0.1.0(@langchain/core@0.3.66(@opentelemetry/api@1.9.0)(openai@4.104.0(ws@8.18.3)(zod@3.25.71))) js-tiktoken: 1.0.20 js-yaml: 4.1.0 jsonpointer: 5.0.1 @@ -18213,7 +18343,7 @@ snapshots: zod: 3.25.71 zod-to-json-schema: 3.24.6(zod@3.25.71) optionalDependencies: - '@langchain/aws': 0.1.11(@langchain/core@0.3.66(@opentelemetry/api@1.9.0)(@opentelemetry/exporter-trace-otlp-proto@0.201.1(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@2.0.1(@opentelemetry/api@1.9.0))(openai@4.100.0(ws@8.18.3)(zod@3.25.71))) + '@langchain/aws': 0.1.11(@langchain/core@0.3.66(@opentelemetry/api@1.9.0)(openai@4.104.0(ws@8.18.3)(zod@3.25.71))) axios: 1.10.0(debug@4.4.1) transitivePeerDependencies: - encoding @@ -20183,7 +20313,7 @@ snapshots: onetime: 5.1.2 signal-exit: 3.0.7 - retry-axios@2.6.0(axios@1.10.0(debug@4.4.1)): + retry-axios@2.6.0(axios@1.10.0): dependencies: axios: 1.10.0(debug@4.4.1) @@ -20758,7 +20888,7 @@ snapshots: babel-jest: 29.7.0(@babel/core@7.27.1) esbuild: 0.25.4 - ts-jest@29.3.4(@babel/core@7.27.1)(@jest/transform@29.7.0)(@jest/types@29.6.3)(babel-jest@29.7.0(@babel/core@7.27.1))(esbuild@0.25.4)(jest@29.7.0(@types/node@20.19.4))(typescript@5.8.2): + ts-jest@29.3.4(@babel/core@7.27.1)(@jest/transform@29.7.0)(@jest/types@29.6.3)(babel-jest@29.7.0(@babel/core@7.27.1))(jest@29.7.0(@types/node@20.19.4))(typescript@5.8.2): dependencies: bs-logger: 0.2.6 ejs: 3.1.10 @@ -20777,7 +20907,26 @@ snapshots: '@jest/transform': 29.7.0 '@jest/types': 29.6.3 babel-jest: 29.7.0(@babel/core@7.27.1) - esbuild: 0.25.4 + + ts-jest@29.3.4(@babel/core@7.27.1)(@jest/transform@29.7.0)(@jest/types@29.6.3)(babel-jest@29.7.0(@babel/core@7.27.1))(jest@29.7.0)(typescript@5.8.2): + dependencies: + bs-logger: 0.2.6 + ejs: 3.1.10 + fast-json-stable-stringify: 2.1.0 + jest: 29.7.0 + jest-util: 29.7.0 + json5: 2.2.3 + lodash.memoize: 4.1.2 + make-error: 1.3.6 + semver: 7.7.2 + type-fest: 4.41.0 + typescript: 5.8.2 + yargs-parser: 21.1.1 + optionalDependencies: + '@babel/core': 7.27.1 + '@jest/transform': 29.7.0 + '@jest/types': 29.6.3 + babel-jest: 29.7.0(@babel/core@7.27.1) ts-poet@6.11.0: dependencies: