agent-api-server 2.1.7__tar.gz → 2.2.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (63) hide show
  1. agent_api_server-2.2.0/PKG-INFO +110 -0
  2. agent_api_server-2.2.0/README.md +70 -0
  3. {agent_api_server-2.1.7 → agent_api_server-2.2.0}/agent_api_server/api/v1/schema.py +15 -1
  4. {agent_api_server-2.1.7 → agent_api_server-2.2.0}/agent_api_server/api/v1/thread.py +1 -2
  5. agent_api_server-2.2.0/agent_api_server/dynamic_llm/model.py +43 -0
  6. {agent_api_server-2.1.7 → agent_api_server-2.2.0}/agent_api_server/log/formatters.py +1 -1
  7. {agent_api_server-2.1.7 → agent_api_server-2.2.0}/agent_api_server/mcp_convert/mcp_convert.py +56 -64
  8. agent_api_server-2.2.0/agent_api_server/mcp_interceptor/mcp_intecerpter.py +20 -0
  9. agent_api_server-2.2.0/agent_api_server/middleware/model.py +59 -0
  10. agent_api_server-2.2.0/agent_api_server/middleware/schema.py +6 -0
  11. agent_api_server-2.2.0/agent_api_server/register/__init__.py +0 -0
  12. agent_api_server-2.2.0/agent_api_server/schema/__init__.py +0 -0
  13. agent_api_server-2.2.0/agent_api_server/schema/context.py +13 -0
  14. agent_api_server-2.2.0/agent_api_server/shared/__init__.py +0 -0
  15. {agent_api_server-2.1.7 → agent_api_server-2.2.0}/agent_api_server/shared/message.py +32 -75
  16. agent_api_server-2.2.0/pyproject.toml +63 -0
  17. agent_api_server-2.1.7/PKG-INFO +0 -130
  18. agent_api_server-2.1.7/README.md +0 -92
  19. agent_api_server-2.1.7/agent_api_server/shared/decode_token.py +0 -107
  20. agent_api_server-2.1.7/pyproject.toml +0 -54
  21. {agent_api_server-2.1.7 → agent_api_server-2.2.0}/agent_api_server/__init__.py +0 -0
  22. {agent_api_server-2.1.7 → agent_api_server-2.2.0}/agent_api_server/api/__init__.py +0 -0
  23. {agent_api_server-2.1.7 → agent_api_server-2.2.0}/agent_api_server/api/v1/__init__.py +0 -0
  24. {agent_api_server-2.1.7 → agent_api_server-2.2.0}/agent_api_server/api/v1/api.py +0 -0
  25. {agent_api_server-2.1.7 → agent_api_server-2.2.0}/agent_api_server/api/v1/config.py +0 -0
  26. {agent_api_server-2.1.7 → agent_api_server-2.2.0}/agent_api_server/api/v1/graph.py +0 -0
  27. {agent_api_server-2.1.7 → agent_api_server-2.2.0}/agent_api_server/cache/__init__.py +0 -0
  28. {agent_api_server-2.1.7 → agent_api_server-2.2.0}/agent_api_server/cache/redis_cache.py +0 -0
  29. {agent_api_server-2.1.7 → agent_api_server-2.2.0}/agent_api_server/callback_handler.py +0 -0
  30. {agent_api_server-2.1.7 → agent_api_server-2.2.0}/agent_api_server/client/css/styles.css +0 -0
  31. {agent_api_server-2.1.7 → agent_api_server-2.2.0}/agent_api_server/client/favicon.ico +0 -0
  32. {agent_api_server-2.1.7 → agent_api_server-2.2.0}/agent_api_server/client/index.html +0 -0
  33. {agent_api_server-2.1.7 → agent_api_server-2.2.0}/agent_api_server/client/js/app.js +0 -0
  34. {agent_api_server-2.1.7 → agent_api_server-2.2.0}/agent_api_server/client/js/index.umd.js +0 -0
  35. {agent_api_server-2.1.7 → agent_api_server-2.2.0}/agent_api_server/config_center/config_center.py +0 -0
  36. {agent_api_server-2.1.7 → agent_api_server-2.2.0}/agent_api_server/configs/__init__.py +0 -0
  37. {agent_api_server-2.1.7 → agent_api_server-2.2.0}/agent_api_server/configs/config.py +0 -0
  38. {agent_api_server-2.1.7 → agent_api_server-2.2.0}/agent_api_server/dynamic_llm/__init__.py +0 -0
  39. {agent_api_server-2.1.7 → agent_api_server-2.2.0}/agent_api_server/dynamic_llm/dynamic_llm.py +0 -0
  40. {agent_api_server-2.1.7 → agent_api_server-2.2.0}/agent_api_server/listener.py +0 -0
  41. {agent_api_server-2.1.7 → agent_api_server-2.2.0}/agent_api_server/log/__init__.py +0 -0
  42. {agent_api_server-2.1.7 → agent_api_server-2.2.0}/agent_api_server/log/logging.json +0 -0
  43. {agent_api_server-2.1.7 → agent_api_server-2.2.0}/agent_api_server/mcp_convert/__init__.py +0 -0
  44. {agent_api_server-2.1.7/agent_api_server/memeory → agent_api_server-2.2.0/agent_api_server/mcp_interceptor}/__init__.py +0 -0
  45. {agent_api_server-2.1.7/agent_api_server/register → agent_api_server-2.2.0/agent_api_server/memeory}/__init__.py +0 -0
  46. {agent_api_server-2.1.7 → agent_api_server-2.2.0}/agent_api_server/memeory/postgres.py +0 -0
  47. {agent_api_server-2.1.7/agent_api_server/shared → agent_api_server-2.2.0/agent_api_server/middleware}/__init__.py +0 -0
  48. {agent_api_server-2.1.7 → agent_api_server-2.2.0}/agent_api_server/register/register.py +0 -0
  49. {agent_api_server-2.1.7 → agent_api_server-2.2.0}/agent_api_server/service.py +0 -0
  50. {agent_api_server-2.1.7 → agent_api_server-2.2.0}/agent_api_server/service_hub/service_hub.py +0 -0
  51. {agent_api_server-2.1.7 → agent_api_server-2.2.0}/agent_api_server/service_hub/service_hub_test.py +0 -0
  52. {agent_api_server-2.1.7 → agent_api_server-2.2.0}/agent_api_server/shared/ase.py +0 -0
  53. {agent_api_server-2.1.7 → agent_api_server-2.2.0}/agent_api_server/shared/base_model.py +0 -0
  54. {agent_api_server-2.1.7 → agent_api_server-2.2.0}/agent_api_server/shared/common.py +0 -0
  55. {agent_api_server-2.1.7 → agent_api_server-2.2.0}/agent_api_server/shared/detect_message.py +0 -0
  56. {agent_api_server-2.1.7 → agent_api_server-2.2.0}/agent_api_server/shared/get_model_info.py +0 -0
  57. {agent_api_server-2.1.7 → agent_api_server-2.2.0}/agent_api_server/shared/util_func.py +0 -0
  58. {agent_api_server-2.1.7 → agent_api_server-2.2.0}/agent_api_server/sso_service/__init__.py +0 -0
  59. {agent_api_server-2.1.7 → agent_api_server-2.2.0}/agent_api_server/sso_service/sdk/__init__.py +0 -0
  60. {agent_api_server-2.1.7 → agent_api_server-2.2.0}/agent_api_server/sso_service/sdk/client.py +0 -0
  61. {agent_api_server-2.1.7 → agent_api_server-2.2.0}/agent_api_server/sso_service/sdk/credential.py +0 -0
  62. {agent_api_server-2.1.7 → agent_api_server-2.2.0}/agent_api_server/sso_service/sdk/encoding.py +0 -0
  63. {agent_api_server-2.1.7 → agent_api_server-2.2.0}/agent_api_server/sso_service/sso_service.py +0 -0
@@ -0,0 +1,110 @@
1
+ Metadata-Version: 2.3
2
+ Name: agent-api-server
3
+ Version: 2.2.0
4
+ Summary: A Langgraph agent API server that implements Langgraph agent's web capabilities and can interact with chatbot
5
+ Keywords: fastapi,langgraph,agent,api-server
6
+ Author: Zijie Zhang
7
+ Author-email: zijie.zhang@advantech.com.cn
8
+ Requires-Python: >=3.11,<3.14
9
+ Classifier: Development Status :: 4 - Beta
10
+ Classifier: Framework :: FastAPI
11
+ Classifier: Programming Language :: Python :: 3
12
+ Classifier: Programming Language :: Python :: 3.11
13
+ Classifier: Programming Language :: Python :: 3.12
14
+ Classifier: Programming Language :: Python :: 3.13
15
+ Requires-Dist: aiofiles (>=24.1.0,<25.0.0)
16
+ Requires-Dist: aiohttp (>=3.13.3,<4.0.0)
17
+ Requires-Dist: authlib (>=1.6.5)
18
+ Requires-Dist: cryptography (>=45.0.4,<46.0.0)
19
+ Requires-Dist: fastapi (>=0.117.0,<0.121.2)
20
+ Requires-Dist: fastmcp (>=2.13.0,<3.0.0)
21
+ Requires-Dist: langchain (>=1.2.0,<2.0.0)
22
+ Requires-Dist: langchain-core (>=1.2.5,<2.0.0)
23
+ Requires-Dist: langchain-mcp-adapters (>=0.2.1,<0.3.0)
24
+ Requires-Dist: langgraph (>=1.0.6,<2.0.0)
25
+ Requires-Dist: langgraph-checkpoint (>=4.0.0,<5.0.0)
26
+ Requires-Dist: langgraph-checkpoint-postgres (>=3.0.3,<4.0.0)
27
+ Requires-Dist: llm-sdk (==1.0.1)
28
+ Requires-Dist: model-manage-client (>=0.0.1.8)
29
+ Requires-Dist: nats-py (>=2.11.0,<3.0.0)
30
+ Requires-Dist: psycopg-binary (>=3.2.9,<4.0.0)
31
+ Requires-Dist: psycopg-pool (>=3.2.6,<4.0.0)
32
+ Requires-Dist: pydantic-settings (>=2.9.1,<3.0.0)
33
+ Requires-Dist: redis (>=6.2.0,<7.0.0)
34
+ Requires-Dist: starlette (>=0.49.3,<0.50.0)
35
+ Requires-Dist: tenacity (>=9.1.2,<10.0.0)
36
+ Project-URL: Homepage, https://gitlab.wise-paas.com/openai/agent_api_server
37
+ Project-URL: Repository, https://gitlab.wise-paas.com/openai/agent_api_server
38
+ Description-Content-Type: text/markdown
39
+
40
+ # agent-api-server
41
+
42
+ `agent-api-server` is a FastAPI-based API server for LangGraph agents. It exposes REST endpoints for thread, schema, graph, and runtime configuration management, and it serves a built-in web client from `/site`.
43
+
44
+ ## Features
45
+
46
+ - FastAPI application factory for embedding or standalone deployment
47
+ - LangGraph-oriented API routes under `/api/v1`
48
+ - Built-in static client assets bundled in both sdist and wheel artifacts
49
+ - Redis-backed thread storage and PostgreSQL checkpoint integration
50
+ - Optional integration with config center, SSO, MCP, and model management services
51
+
52
+ ## Requirements
53
+
54
+ - Python 3.11 to 3.13
55
+ - Redis
56
+ - PostgreSQL
57
+ - Access to all runtime dependencies declared in `pyproject.toml`
58
+
59
+ ## Installation
60
+
61
+ Install from a package index that provides all required dependencies:
62
+
63
+ ```bash
64
+ pip install agent-api-server
65
+ ```
66
+
67
+ This project depends on `llm-sdk` and `model-manage-client`. If those packages are hosted on a private index in your environment, configure `pip` or Poetry to use that index before installation.
68
+
69
+ ## Configuration
70
+
71
+ The server reads configuration from environment variables. Common settings include:
72
+
73
+ ```env
74
+ REDIS_URL=redis://localhost:6379/0
75
+ POSTGRES_URL=postgresql://postgres:postgres@localhost:5432/postgres
76
+ MODEL_MANAGER_SERVICE_URL=http://127.0.0.1:10053
77
+ SERVER_PORT=8080
78
+ SERVER_WORKER_AMOUNT=1
79
+ LOG_LEVEL=INFO
80
+ ENABLE_MCP_SERVER=False
81
+ ```
82
+
83
+ See `.env_example` for a more complete example.
84
+
85
+ ## Running the server
86
+
87
+ Run the application with Uvicorn:
88
+
89
+ ```bash
90
+ uvicorn agent_api_server.service:create_fastapi_app --factory --host 0.0.0.0 --port 8080
91
+ ```
92
+
93
+ After startup:
94
+
95
+ - API root: `http://127.0.0.1:8080/api/v1`
96
+ - OpenAPI docs: `http://127.0.0.1:8080/docs`
97
+ - Built-in client: `http://127.0.0.1:8080/site`
98
+
99
+ ## Build
100
+
101
+ Build source and wheel distributions with Poetry:
102
+
103
+ ```bash
104
+ poetry build
105
+ ```
106
+
107
+ ## Repository
108
+
109
+ Source repository: <https://gitlab.wise-paas.com/openai/agent_api_server>
110
+
@@ -0,0 +1,70 @@
1
+ # agent-api-server
2
+
3
+ `agent-api-server` is a FastAPI-based API server for LangGraph agents. It exposes REST endpoints for thread, schema, graph, and runtime configuration management, and it serves a built-in web client from `/site`.
4
+
5
+ ## Features
6
+
7
+ - FastAPI application factory for embedding or standalone deployment
8
+ - LangGraph-oriented API routes under `/api/v1`
9
+ - Built-in static client assets bundled in both sdist and wheel artifacts
10
+ - Redis-backed thread storage and PostgreSQL checkpoint integration
11
+ - Optional integration with config center, SSO, MCP, and model management services
12
+
13
+ ## Requirements
14
+
15
+ - Python 3.11 to 3.13
16
+ - Redis
17
+ - PostgreSQL
18
+ - Access to all runtime dependencies declared in `pyproject.toml`
19
+
20
+ ## Installation
21
+
22
+ Install from a package index that provides all required dependencies:
23
+
24
+ ```bash
25
+ pip install agent-api-server
26
+ ```
27
+
28
+ This project depends on `llm-sdk` and `model-manage-client`. If those packages are hosted on a private index in your environment, configure `pip` or Poetry to use that index before installation.
29
+
30
+ ## Configuration
31
+
32
+ The server reads configuration from environment variables. Common settings include:
33
+
34
+ ```env
35
+ REDIS_URL=redis://localhost:6379/0
36
+ POSTGRES_URL=postgresql://postgres:postgres@localhost:5432/postgres
37
+ MODEL_MANAGER_SERVICE_URL=http://127.0.0.1:10053
38
+ SERVER_PORT=8080
39
+ SERVER_WORKER_AMOUNT=1
40
+ LOG_LEVEL=INFO
41
+ ENABLE_MCP_SERVER=False
42
+ ```
43
+
44
+ See `.env_example` for a more complete example.
45
+
46
+ ## Running the server
47
+
48
+ Run the application with Uvicorn:
49
+
50
+ ```bash
51
+ uvicorn agent_api_server.service:create_fastapi_app --factory --host 0.0.0.0 --port 8080
52
+ ```
53
+
54
+ After startup:
55
+
56
+ - API root: `http://127.0.0.1:8080/api/v1`
57
+ - OpenAPI docs: `http://127.0.0.1:8080/docs`
58
+ - Built-in client: `http://127.0.0.1:8080/site`
59
+
60
+ ## Build
61
+
62
+ Build source and wheel distributions with Poetry:
63
+
64
+ ```bash
65
+ poetry build
66
+ ```
67
+
68
+ ## Repository
69
+
70
+ Source repository: <https://gitlab.wise-paas.com/openai/agent_api_server>
@@ -35,8 +35,22 @@ async def get_graph_schema(graph_name: str):
35
35
  actual_type=type(graph_instance).__name__
36
36
  )
37
37
 
38
+ input_schema = graph_instance.get_input_jsonschema()
39
+ if input_schema:
40
+ if input_schema.get("properties"):
41
+ input_schema["properties"].pop("messages")
42
+ if input_schema.get("required"):
43
+ required_schema = input_schema.get("required")
44
+
45
+ real_required_schema = []
46
+ for _, schema in enumerate(required_schema):
47
+ if schema == "messages":
48
+ continue
49
+ real_required_schema.append(schema)
50
+ input_schema["required"] = real_required_schema
51
+
38
52
  return JSONResponse(
39
- content=graph_instance.get_input_jsonschema(),
53
+ content=input_schema,
40
54
  media_type="application/schema+json",
41
55
  )
42
56
  except ValueError as ve:
@@ -276,8 +276,7 @@ async def stream(
276
276
  404: {"model": error_response, "description": "Thread not found"},
277
277
  408: {"model": error_response, "description": "Request timeout"},
278
278
  500: {"model": error_response, "description": "Execution failed"}
279
- },
280
- deprecated=True
279
+ }
281
280
  )
282
281
  async def run_thread(
283
282
  thread_id: str,
@@ -0,0 +1,43 @@
1
+ from langchain_core.language_models import BaseChatModel
2
+ from langchain_openai import ChatOpenAI
3
+ from llm_sdk.llm import ModelInstance
4
+ from llm_sdk.model_providers.base import ConfigType
5
+ from pydantic import SecretStr
6
+
7
+ from agent_api_server.dynamic_llm.dynamic_llm import DynamicLLM
8
+ from agent_api_server.schema.context import Context
9
+
10
+ def get_init_model()->BaseChatModel:
11
+ """
12
+ Get the initial chat model instance.
13
+ """
14
+ return ChatOpenAI(
15
+ model="gpt-4o-2024-05-13",
16
+ max_tokens=20000,
17
+ api_key=SecretStr("xxx")
18
+ )
19
+
20
+ def get_chat_model_instance(context: Context)-> ModelInstance:
21
+ """"
22
+ Get the chat model instance for the given context.
23
+ """
24
+
25
+ dynamic_llm = DynamicLLM(tool_name="default",config_type=ConfigType.CHAT,use_sys_llm=context.use_system_llm)
26
+
27
+ llm_config = {
28
+ "agent_id":context.graph_name,
29
+ "ts_tenant":context.ts_tenant,
30
+ "provider":context.config.get(dynamic_llm._get_config_key("PROVIDER", context.ts_tenant)),
31
+ "model": context.config.get(dynamic_llm._get_config_key("MODEL", context.ts_tenant)),
32
+ "credentials": context.config.get(dynamic_llm._get_config_key("CREDENTIALS", context.ts_tenant)),
33
+ }
34
+ with dynamic_llm._get_model_instance(llm_config,config={}) as model_instance:
35
+ return model_instance
36
+
37
+
38
+ def get_model_credentials(context: Context)-> dict:
39
+ """
40
+ Get the model credentials for the given context.
41
+ """
42
+ model_instance = get_chat_model_instance(context)
43
+ return model_instance.unified_credential()
@@ -90,7 +90,7 @@ class ColorFormatter(logging.Formatter):
90
90
 
91
91
  log_template = (
92
92
  "{timestamp_color}{timestamp}{reset} "
93
- "[{level_color}{levelname:8}{reset}] "
93
+ "[{level_color}{levelname}{reset}] "
94
94
  "{message} "
95
95
  "[{module_color}{module}{reset}] "
96
96
  "thread_name={thread_color}{thread}{reset}"
@@ -1,18 +1,17 @@
1
- import os
2
1
  import inspect
3
2
  import logging
4
3
  import json
5
4
  from typing import Dict, Any, Optional, List, Callable, Awaitable
6
5
  from functools import wraps, partial
7
6
  from datetime import datetime
8
- from langfuse import propagate_attributes
9
- from langfuse.langchain import CallbackHandler
10
- from agent_api_server.shared.decode_token import decode_jwt
7
+
11
8
  from langgraph.config import get_stream_writer
12
9
  from langgraph.graph.state import CompiledStateGraph
13
- from fastmcp import FastMCP, Context
10
+ from fastmcp import FastMCP,Context
14
11
  from mcp import types
15
12
  from mcp.types import RequestParams, CallToolResult, TextContent
13
+
14
+ from agent_api_server.schema.context import Context as RuntimeContext
16
15
  from agent_api_server.shared.message import handle_stream_event
17
16
  from agent_api_server.shared.util_func import load_graph_config, load_graph, get_env
18
17
 
@@ -100,12 +99,15 @@ async def _execute_graph_tool(
100
99
  Execution result dict
101
100
  """
102
101
  ctx = Context(fastmcp=mcp)
102
+
103
103
  from fastmcp.server.dependencies import get_http_request
104
104
  request = get_http_request()
105
105
 
106
- use_sys_llm = request.headers.get("UseSysLLM", "")
106
+ use_sys_llm = request.headers.get("UseSysLLM", None)
107
107
  ts_tenant = request.headers.get("TSTenant", "")
108
108
  ei_token = request.headers.get("Authorization", "")
109
+ if ei_token.startswith("Bearer "):
110
+ ei_token = ei_token.removeprefix("Bearer ")
109
111
  thread_id = request.headers.get('thread_id', '')
110
112
  start_time = datetime.now()
111
113
 
@@ -113,6 +115,20 @@ async def _execute_graph_tool(
113
115
 
114
116
  # Validate input
115
117
  schema = graph_instance.get_input_jsonschema()
118
+ if schema:
119
+ if schema.get("properties"):
120
+ if schema["properties"].get("messages"):
121
+ schema["properties"].pop("messages")
122
+ if schema.get("required"):
123
+ required_schema = schema.get("required")
124
+
125
+ real_required_schema = []
126
+ for _, s in enumerate(required_schema):
127
+ if s == "messages":
128
+ continue
129
+ real_required_schema.append(s)
130
+ schema["required"] = real_required_schema
131
+
116
132
  input_dict = {}
117
133
  validation_errors = []
118
134
 
@@ -139,19 +155,6 @@ async def _execute_graph_tool(
139
155
  thread_id = ctx.session_id
140
156
  logger.info(f"execute graph {graph_name} with session id {ctx.session_id}")
141
157
 
142
- user_id = None
143
- if ei_token:
144
- try:
145
- header, payload, user_info = decode_jwt(ei_token)
146
- if user_info and 'id' in user_info:
147
- user_id = user_info['id']
148
- logger.info(f"Extracted user_id from token: {user_id}")
149
- else:
150
- logger.warning("No user_id found in JWT token")
151
- except Exception as e:
152
- logger.warning(f"Failed to decode JWT token: {str(e)}")
153
-
154
- # Build configurable parameters
155
158
  configurable_params = {
156
159
  "use_sys_llm": use_sys_llm,
157
160
  "thread_id": thread_id,
@@ -166,53 +169,28 @@ async def _execute_graph_tool(
166
169
  **configurable_params
167
170
  }
168
171
 
169
- logger.info(f"finally configurable parameter is {configurable_params}")
170
-
171
- # Configure Langfuse callbacks if available
172
- langfuse_keys = [
173
- os.getenv("LANGFUSE_SECRET_KEY"),
174
- os.getenv("LANGFUSE_PUBLIC_KEY"),
175
- os.getenv("LANGFUSE_BASE_URL")
176
- ]
177
-
178
- callbacks_config = {}
179
- if all(langfuse_keys):
180
- langfuse_handler = CallbackHandler()
181
- callbacks_config = {"callbacks": [langfuse_handler], "run_name": f"{graph_name}_MCP_Call"}
182
-
183
172
  chunks = []
184
- config = {"configurable": configurable_params, **callbacks_config}
185
173
  try:
186
- async def process_stream():
187
- if user_id:
188
- logger.info(f"execute graph {graph_name} with user_id: {user_id}")
189
- with propagate_attributes(session_id=thread_id, user_id=user_id, trace_name=f"{graph_name}_MCP_Call"):
190
- async for stream_event in graph_instance.astream(
191
- input_dict,
192
- config=config,
193
- stream_mode=["updates"],
194
- subgraphs=True
195
- ):
196
- async for chunk in handle_stream_event(stream_event):
197
- yield chunk
198
-
199
- else:
200
- logger.info(f"execute graph {graph_name} without user_id")
201
- with propagate_attributes(session_id=thread_id, trace_name=f"{graph_name}_MCP_Call"):
202
- async for stream_event in graph_instance.astream(
203
- input_dict,
204
- config=config,
205
- stream_mode=["updates"],
206
- subgraphs=True
207
- ):
208
- async for chunk in handle_stream_event(stream_event):
209
- yield chunk
210
-
211
-
212
- async for chunk in process_stream():
213
- ctx.request_context.meta = RequestParams.Meta(progressToken=ctx.request_id)
214
- await ctx.report_progress(message=chunk, progress=len(chunks))
215
- chunks.append(chunk)
174
+ async for stream_event in graph_instance.astream(
175
+ input_dict,
176
+ config={
177
+ "configurable": configurable_params
178
+ },
179
+ context=RuntimeContext(
180
+ use_system_llm=configurable_params.get("use_sys_llm"),
181
+ ts_tenant=configurable_params.get("TSTenant"),
182
+ ei_token=configurable_params.get("EIToken"),
183
+ input=input_dict,
184
+ config=configurable_params,
185
+ graph_name=configurable_params.get("graph_name"),
186
+ ),
187
+ stream_mode=["updates"],
188
+ subgraphs=True
189
+ ):
190
+ async for chunk in handle_stream_event(stream_event):
191
+ ctx.request_context.meta = RequestParams.Meta(progressToken=ctx.request_id)
192
+ await ctx.report_progress(message=chunk, progress=len(chunks))
193
+ chunks.append(chunk)
216
194
 
217
195
  if not chunks:
218
196
  raise ValueError("No response from graph execution")
@@ -251,6 +229,20 @@ def create_tool_from_schema(
251
229
  func_doc: str = "",
252
230
  implementation: Optional[Callable[..., Awaitable[Dict[str, Any]]]] = None
253
231
  ) -> Callable[..., Awaitable[Dict[str, Any]]]:
232
+ if schema:
233
+ if schema.get("properties"):
234
+ if schema["properties"].get("messages"):
235
+ schema["properties"].pop("messages")
236
+ if schema.get("required"):
237
+ required_schema = schema.get("required")
238
+
239
+ real_required_schema = []
240
+ for _, s in enumerate(required_schema):
241
+ if s == "messages":
242
+ continue
243
+ real_required_schema.append(s)
244
+ schema["required"] = real_required_schema
245
+
254
246
  properties = schema.get("properties", {})
255
247
  required = schema.get("required", [])
256
248
  type_map = {
@@ -0,0 +1,20 @@
1
+ from langchain_mcp_adapters.interceptors import MCPToolCallRequest
2
+
3
+ async def inject_authorization(
4
+ request: MCPToolCallRequest,
5
+ handler,
6
+ ):
7
+ """Inject user credentials into MCP tool calls."""
8
+ runtime = request.runtime
9
+ authorization = runtime.context.ei_token
10
+ tenant_id = runtime.context.ts_tenant
11
+
12
+ if request.headers:
13
+ headers = {**request.headers, "TSTenant": tenant_id, "Authorization": f"Bearer {authorization}"}
14
+ else:
15
+ headers = {"TSTenant": tenant_id, "Authorization": f"Bearer {authorization}"}
16
+ # Add user context to tool arguments
17
+ modified_request = request.override(
18
+ headers={**headers}
19
+ )
20
+ return await handler(modified_request)
@@ -0,0 +1,59 @@
1
+ from langchain_core.messages import HumanMessage
2
+
3
+ from agent_api_server.middleware.schema import InputState
4
+ from agent_api_server.schema.context import Context
5
+ from agent_api_server.dynamic_llm.model import get_chat_model_instance
6
+ from langchain.agents.middleware import AgentMiddleware, ModelRequest, ModelResponse, wrap_tool_call
7
+ from typing import Callable
8
+
9
+ class ModelMiddleware(AgentMiddleware[InputState]):
10
+ state_schema = InputState
11
+
12
+ def __init__(self, use_system_llm:bool=False):
13
+ super().__init__()
14
+ self.use_system_llm = use_system_llm
15
+
16
+ async def abefore_agent(self,state: InputState) -> ModelRequest:
17
+ human_message = [HumanMessage(content=state["user_input"])]
18
+ return {
19
+ "messages": [
20
+ *state["messages"],
21
+ *human_message
22
+ ]
23
+ }
24
+
25
+ async def awrap_model_call(
26
+ self,
27
+ request: ModelRequest,
28
+ handler: Callable[[ModelRequest], ModelResponse],
29
+ ) -> ModelResponse:
30
+ context: Context = request.runtime.context
31
+ if context.use_system_llm is None:
32
+ context.use_system_llm = self.use_system_llm
33
+
34
+ model_instance = get_chat_model_instance(context)
35
+
36
+ model_settings = request.model_settings.copy()
37
+ model_settings.pop("cache_control", None)
38
+
39
+ return await handler(request.override(model=model_instance, model_settings=model_settings))
40
+
41
+ class ChangeModel(AgentMiddleware):
42
+ def __init__(self, use_system_llm: bool = False):
43
+ super().__init__()
44
+ self.use_system_llm = use_system_llm
45
+
46
+ async def awrap_model_call(
47
+ self,
48
+ request: ModelRequest,
49
+ handler: Callable[[ModelRequest], ModelResponse],
50
+ ) -> ModelResponse:
51
+ context: Context = request.runtime.context
52
+ context.use_system_llm = self.use_system_llm
53
+
54
+ model_instance = get_chat_model_instance(context)
55
+
56
+ model_settings = request.model_settings.copy()
57
+ model_settings.pop("cache_control", None)
58
+
59
+ return await handler(request.override(model=model_instance, model_settings=model_settings))
@@ -0,0 +1,6 @@
1
+ from langchain.agents import AgentState
2
+ from typing_extensions import Required
3
+
4
+
5
+ class InputState(AgentState):
6
+ user_input: Required[str]
@@ -0,0 +1,13 @@
1
+ from __future__ import annotations
2
+ from typing import Optional
3
+ from dataclasses import dataclass
4
+
5
+ @dataclass
6
+ class Context:
7
+ ts_tenant: str
8
+ use_system_llm: Optional[bool] = None
9
+ input: Optional[dict] = None
10
+ ei_token: Optional[str] = None
11
+ graph_name: Optional[str] = None
12
+ files: Optional[list[dict]] = None
13
+ config: Optional[dict] = None