diff --git a/agent-openai-agents-sdk/agent_server/agent.py b/agent-openai-agents-sdk/agent_server/agent.py index ddd67a7b..72836b4e 100644 --- a/agent-openai-agents-sdk/agent_server/agent.py +++ b/agent-openai-agents-sdk/agent_server/agent.py @@ -41,7 +41,7 @@ def create_coding_agent(mcp_server: McpServer) -> Agent: @invoke() -async def invoke(request: ResponsesAgentRequest) -> ResponsesAgentResponse: +async def invoke_handler(request: ResponsesAgentRequest) -> ResponsesAgentResponse: # Optionally use the user's workspace client for on-behalf-of authentication # user_workspace_client = get_user_workspace_client() async with await init_mcp_server() as mcp_server: @@ -52,7 +52,7 @@ async def invoke(request: ResponsesAgentRequest) -> ResponsesAgentResponse: @stream() -async def stream(request: dict) -> AsyncGenerator[ResponsesAgentStreamEvent, None]: +async def stream_handler(request: dict) -> AsyncGenerator[ResponsesAgentStreamEvent, None]: # Optionally use the user's workspace client for on-behalf-of authentication # user_workspace_client = get_user_workspace_client() async with await init_mcp_server() as mcp_server: diff --git a/agent-openai-agents-sdk/agent_server/app.py b/agent-openai-agents-sdk/agent_server/app.py new file mode 100644 index 00000000..353568b4 --- /dev/null +++ b/agent-openai-agents-sdk/agent_server/app.py @@ -0,0 +1,28 @@ +from fastapi.responses import StreamingResponse +from aiohttp.web import Request +from mlflow.genai.agent_server import AgentServer + + +class MlflowAgentServer(AgentServer): + def __init__(self, *args, **kwargs): + super().__init__(*args, **kwargs) + self._initialized = False + + async def _handle_invocations_request(self, *args, **kwargs): + if not self._initialized: + self._initialized = True + from mlflow.genai.agent_server import setup_mlflow_git_based_version_tracking + setup_mlflow_git_based_version_tracking() + return await super()._handle_invocations_request(*args, **kwargs) + + +def create_app(): + # Need to import the agent to register the functions with the server + import agent_server.agent + from dotenv import load_dotenv + + # Load env vars from .env before importing the agent for proper auth + load_dotenv(dotenv_path=".env", override=True) + + server = MlflowAgentServer("ResponsesAgent", enable_chat_proxy=True) + return server.app diff --git a/agent-openai-agents-sdk/agent_server/start_server.py b/agent-openai-agents-sdk/agent_server/start_server.py deleted file mode 100644 index 1d5ecd0c..00000000 --- a/agent-openai-agents-sdk/agent_server/start_server.py +++ /dev/null @@ -1,17 +0,0 @@ -from dotenv import load_dotenv -from mlflow.genai.agent_server import AgentServer, setup_mlflow_git_based_version_tracking - -# Load env vars from .env before importing the agent for proper auth -load_dotenv(dotenv_path=".env", override=True) - -# Need to import the agent to register the functions with the server -import agent_server.agent # noqa: E402 - -agent_server = AgentServer("ResponsesAgent", enable_chat_proxy=True) -# Define the app as a module level variable to enable multiple workers -app = agent_server.app # noqa: F841 -setup_mlflow_git_based_version_tracking() - - -def main(): - agent_server.run(app_import_string="agent_server.start_server:app") diff --git a/agent-openai-agents-sdk/app.yaml b/agent-openai-agents-sdk/app.yaml index 34465373..65482a46 100644 --- a/agent-openai-agents-sdk/app.yaml +++ b/agent-openai-agents-sdk/app.yaml @@ -1,4 +1,4 @@ -command: ["uv", "run", "start-app"] +command: ["python", "scripts/start_app.py"] # databricks apps listen by default on port 8000 env: diff --git a/agent-openai-agents-sdk/pyproject.toml b/agent-openai-agents-sdk/pyproject.toml index c8c059d8..52d431d1 100644 --- a/agent-openai-agents-sdk/pyproject.toml +++ b/agent-openai-agents-sdk/pyproject.toml @@ -9,7 +9,7 @@ authors = [ requires-python = ">=3.11" dependencies = [ "fastapi>=0.115.12", - "uvicorn>=0.34.2", + "uvicorn[standard]>=0.34.2", "databricks-openai>=0.9.0", "mlflow>=3.9.0", "openai-agents>=0.4.1", @@ -33,3 +33,6 @@ start-app = "scripts.start_app:main" start-server = "agent_server.start_server:main" agent-evaluate = "agent_server.evaluate_agent:evaluate" discover-tools = "scripts.discover_tools:main" + +[tool.uv.pip] +output-file = "requirements.txt" diff --git a/agent-openai-agents-sdk/requirements.txt b/agent-openai-agents-sdk/requirements.txt index 60cc5e6a..4b049852 100644 --- a/agent-openai-agents-sdk/requirements.txt +++ b/agent-openai-agents-sdk/requirements.txt @@ -1 +1,514 @@ -uv +# This file was autogenerated by uv via the following command: +# uv pip compile pyproject.toml +aiohappyeyeballs==2.6.1 + # via aiohttp +aiohttp==3.13.3 + # via + # aiohttp-retry + # unitycatalog-client +aiohttp-retry==2.9.1 + # via unitycatalog-client +aiosignal==1.4.0 + # via aiohttp +alembic==1.18.3 + # via mlflow +annotated-doc==0.0.4 + # via fastapi +annotated-types==0.7.0 + # via pydantic +anyio==4.12.1 + # via + # httpx + # mcp + # openai + # sse-starlette + # starlette + # watchfiles +attrs==25.4.0 + # via + # aiohttp + # jsonschema + # referencing +blinker==1.9.0 + # via flask +cachetools==6.2.6 + # via + # mlflow-skinny + # mlflow-tracing +certifi==2026.1.4 + # via + # httpcore + # httpx + # requests +cffi==2.0.0 + # via cryptography +charset-normalizer==3.4.4 + # via requests +click==8.3.1 + # via + # flask + # mlflow-skinny + # uvicorn +cloudpickle==3.1.2 + # via + # mlflow-skinny + # unitycatalog-ai +colorama==0.4.6 + # via griffe +contourpy==1.3.3 + # via matplotlib +cryptography==46.0.4 + # via + # google-auth + # mlflow + # pyjwt +cycler==0.12.1 + # via matplotlib +databricks-ai-bridge==0.13.0 + # via + # databricks-mcp + # databricks-openai +databricks-connect==16.1.7 + # via unitycatalog-ai +databricks-mcp==0.7.0 + # via databricks-openai +databricks-openai==0.10.0 + # via agent-server (pyproject.toml) +databricks-sdk==0.85.0 + # via + # databricks-ai-bridge + # databricks-connect + # databricks-mcp + # mlflow-skinny + # mlflow-tracing + # unitycatalog-ai +databricks-vectorsearch==0.64 + # via + # databricks-ai-bridge + # databricks-openai +deprecation==2.1.0 + # via databricks-vectorsearch +distro==1.9.0 + # via openai +docker==7.1.0 + # via mlflow +fastapi==0.128.2 + # via + # agent-server (pyproject.toml) + # mlflow-skinny +flask==3.1.2 + # via + # flask-cors + # mlflow +flask-cors==6.0.2 + # via mlflow +fonttools==4.61.1 + # via matplotlib +frozenlist==1.8.0 + # via + # aiohttp + # aiosignal +gitdb==4.0.12 + # via gitpython +gitpython==3.1.46 + # via mlflow-skinny +google-auth==2.48.0 + # via databricks-sdk +googleapis-common-protos==1.72.0 + # via + # databricks-connect + # grpcio-status +graphene==3.4.3 + # via mlflow +graphql-core==3.2.7 + # via + # graphene + # graphql-relay +graphql-relay==3.2.0 + # via graphene +griffe==1.15.0 + # via openai-agents +grpcio==1.76.0 + # via + # databricks-connect + # grpcio-status +grpcio-status==1.76.0 + # via databricks-connect +gunicorn==23.0.0 + # via mlflow +h11==0.16.0 + # via + # httpcore + # uvicorn +httpcore==1.0.9 + # via httpx +httptools==0.7.1 + # via uvicorn +httpx==0.28.1 + # via + # langgraph-sdk + # langsmith + # mcp + # openai +httpx-sse==0.4.3 + # via mcp +huey==2.6.0 + # via mlflow +idna==3.11 + # via + # anyio + # httpx + # requests + # yarl +importlib-metadata==8.7.1 + # via + # mlflow-skinny + # opentelemetry-api +itsdangerous==2.2.0 + # via flask +jinja2==3.1.6 + # via flask +jiter==0.13.0 + # via openai +joblib==1.5.3 + # via scikit-learn +jsonpatch==1.33 + # via langchain-core +jsonpointer==3.0.0 + # via jsonpatch +jsonschema==4.26.0 + # via mcp +jsonschema-specifications==2025.9.1 + # via jsonschema +kiwisolver==1.4.9 + # via matplotlib +langchain==1.2.8 + # via databricks-openai +langchain-core==1.2.9 + # via + # langchain + # langgraph + # langgraph-checkpoint + # langgraph-prebuilt +langgraph==1.0.7 + # via langchain +langgraph-checkpoint==4.0.0 + # via + # langgraph + # langgraph-prebuilt +langgraph-prebuilt==1.0.7 + # via langgraph +langgraph-sdk==0.3.4 + # via langgraph +langsmith==0.6.9 + # via langchain-core +mako==1.3.10 + # via alembic +markupsafe==3.0.3 + # via + # flask + # jinja2 + # mako + # werkzeug +matplotlib==3.10.8 + # via mlflow +mcp==1.26.0 + # via + # databricks-mcp + # openai-agents +mlflow==3.9.0 + # via + # agent-server (pyproject.toml) + # databricks-mcp + # databricks-openai +mlflow-skinny==3.9.0 + # via + # databricks-ai-bridge + # databricks-vectorsearch + # mlflow +mlflow-tracing==3.9.0 + # via mlflow +multidict==6.7.1 + # via + # aiohttp + # yarl +nest-asyncio==1.6.0 + # via unitycatalog-ai +numpy==1.26.4 + # via + # contourpy + # databricks-connect + # matplotlib + # mlflow + # pandas + # scikit-learn + # scipy + # skops +openai==2.17.0 + # via + # databricks-openai + # openai-agents + # unitycatalog-openai +openai-agents==0.8.0 + # via + # agent-server (pyproject.toml) + # databricks-openai +opentelemetry-api==1.39.1 + # via + # mlflow-skinny + # mlflow-tracing + # opentelemetry-sdk + # opentelemetry-semantic-conventions +opentelemetry-proto==1.39.1 + # via + # mlflow-skinny + # mlflow-tracing +opentelemetry-sdk==1.39.1 + # via + # mlflow-skinny + # mlflow-tracing +opentelemetry-semantic-conventions==0.60b1 + # via opentelemetry-sdk +orjson==3.11.7 + # via + # langgraph-sdk + # langsmith +ormsgpack==1.12.2 + # via langgraph-checkpoint +packaging==25.0 + # via + # databricks-connect + # deprecation + # gunicorn + # langchain-core + # langsmith + # matplotlib + # mlflow-skinny + # mlflow-tracing + # skops +pandas==2.3.3 + # via + # databricks-ai-bridge + # databricks-connect + # mlflow + # unitycatalog-ai +pillow==12.1.0 + # via matplotlib +prettytable==3.17.0 + # via skops +propcache==0.4.1 + # via + # aiohttp + # yarl +protobuf==6.33.5 + # via + # databricks-sdk + # databricks-vectorsearch + # googleapis-common-protos + # grpcio-status + # mlflow-skinny + # mlflow-tracing + # opentelemetry-proto +py4j==0.10.9.7 + # via databricks-connect +pyarrow==22.0.0 + # via + # databricks-connect + # mlflow +pyasn1==0.6.2 + # via + # pyasn1-modules + # rsa +pyasn1-modules==0.4.2 + # via google-auth +pycparser==3.0 + # via cffi +pydantic==2.12.5 + # via + # databricks-ai-bridge + # databricks-openai + # fastapi + # langchain + # langchain-core + # langgraph + # langsmith + # mcp + # mlflow-skinny + # mlflow-tracing + # openai + # openai-agents + # pydantic-settings + # unitycatalog-ai + # unitycatalog-client + # unitycatalog-openai +pydantic-core==2.41.5 + # via pydantic +pydantic-settings==2.12.0 + # via mcp +pyjwt==2.11.0 + # via mcp +pyparsing==3.3.2 + # via matplotlib +python-dateutil==2.9.0.post0 + # via + # graphene + # matplotlib + # pandas + # unitycatalog-client +python-dotenv==1.2.1 + # via + # agent-server (pyproject.toml) + # mlflow-skinny + # pydantic-settings + # uvicorn +python-multipart==0.0.22 + # via mcp +pytz==2025.2 + # via pandas +pyyaml==6.0.3 + # via + # langchain-core + # mlflow-skinny + # uvicorn +referencing==0.37.0 + # via + # jsonschema + # jsonschema-specifications +regex==2026.1.15 + # via tiktoken +requests==2.32.5 + # via + # databricks-sdk + # databricks-vectorsearch + # docker + # langsmith + # mlflow-skinny + # openai-agents + # requests-toolbelt + # tiktoken +requests-toolbelt==1.0.0 + # via langsmith +rpds-py==0.30.0 + # via + # jsonschema + # referencing +rsa==4.9.1 + # via google-auth +scikit-learn==1.8.0 + # via + # mlflow + # skops +scipy==1.17.0 + # via + # mlflow + # scikit-learn + # skops +setuptools==80.10.2 + # via databricks-connect +six==1.17.0 + # via + # databricks-connect + # python-dateutil +skops==0.13.0 + # via mlflow +smmap==5.0.2 + # via gitdb +sniffio==1.3.1 + # via openai +sqlalchemy==2.0.46 + # via + # alembic + # mlflow +sqlparse==0.5.5 + # via mlflow-skinny +sse-starlette==3.2.0 + # via mcp +starlette==0.50.0 + # via + # fastapi + # mcp + # sse-starlette +tabulate==0.9.0 + # via databricks-ai-bridge +tenacity==9.1.3 + # via langchain-core +threadpoolctl==3.6.0 + # via scikit-learn +tiktoken==0.12.0 + # via databricks-ai-bridge +tqdm==4.67.3 + # via openai +types-requests==2.32.4.20260107 + # via openai-agents +typing-extensions==4.15.0 + # via + # alembic + # databricks-ai-bridge + # fastapi + # graphene + # grpcio + # langchain-core + # mcp + # mlflow-skinny + # openai + # openai-agents + # opentelemetry-api + # opentelemetry-sdk + # opentelemetry-semantic-conventions + # pydantic + # pydantic-core + # sqlalchemy + # typing-inspection + # unitycatalog-ai + # unitycatalog-client +typing-inspection==0.4.2 + # via + # fastapi + # mcp + # pydantic + # pydantic-settings +tzdata==2025.3 + # via pandas +unitycatalog-ai==0.3.2 + # via unitycatalog-openai +unitycatalog-client==0.3.1 + # via unitycatalog-ai +unitycatalog-openai==0.2.0 + # via databricks-openai +urllib3==2.6.3 + # via + # docker + # requests + # types-requests + # unitycatalog-client +uuid-utils==0.14.0 + # via + # langchain-core + # langsmith +uvicorn==0.40.0 + # via + # agent-server (pyproject.toml) + # mcp + # mlflow-skinny +uvloop==0.22.1 + # via uvicorn +watchfiles==1.1.1 + # via uvicorn +wcwidth==0.5.3 + # via prettytable +websockets==16.0 + # via uvicorn +werkzeug==3.1.5 + # via + # flask + # flask-cors +xxhash==3.6.0 + # via + # langgraph + # langsmith +yarl==1.22.0 + # via aiohttp +zipp==3.23.0 + # via importlib-metadata +zstandard==0.25.0 + # via langsmith diff --git a/agent-openai-agents-sdk/scripts/start_app.py b/agent-openai-agents-sdk/scripts/start_app.py index 9fe60cde..51526e13 100644 --- a/agent-openai-agents-sdk/scripts/start_app.py +++ b/agent-openai-agents-sdk/scripts/start_app.py @@ -13,6 +13,7 @@ All options are passed through to the backend server (start-server). See 'uv run start-server --help' for available options. """ +from time import sleep import argparse import os @@ -113,7 +114,7 @@ def clone_frontend_if_needed(self): def start_process(self, cmd, name, log_file, patterns, cwd=None): print(f"Starting {name}...") process = subprocess.Popen( - cmd, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, text=True, bufsize=1, cwd=cwd + cmd, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, text=True, cwd=cwd ) thread = threading.Thread( @@ -153,19 +154,13 @@ def cleanup(self): def run(self, backend_args=None): load_dotenv(dotenv_path=".env", override=True) - if not self.clone_frontend_if_needed(): - return 1 - - # Set API_PROXY environment variable for frontend to connect to backend - os.environ["API_PROXY"] = f"http://localhost:{self.port}/invocations" - # Open log files self.backend_log = open("backend.log", "w", buffering=1) self.frontend_log = open("frontend.log", "w", buffering=1) try: # Build backend command, passing through all arguments - backend_cmd = ["uv", "run", "start-server"] + backend_cmd = ["uvicorn", "--factory", "agent_server.app:create_app"] if backend_args: backend_cmd.extend(backend_args) @@ -174,6 +169,15 @@ def run(self, backend_args=None): backend_cmd, "backend", self.backend_log, BACKEND_READY ) + # Allow uvicorn server to start first + sleep(2) + + if not self.clone_frontend_if_needed(): + return 1 + + # Set API_PROXY environment variable for frontend to connect to backend + os.environ["API_PROXY"] = f"http://localhost:{self.port}/invocations" + # Setup and start frontend frontend_dir = Path("e2e-chatbot-app-next") for cmd, desc in [("npm install", "install"), ("npm run build", "build")]: