agent-api-server 2.2.1__tar.gz → 2.2.1a2__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.
- agent_api_server-2.2.1a2/PKG-INFO +308 -0
- agent_api_server-2.2.1a2/README.md +275 -0
- agent_api_server-2.2.1a2/adapters/__init__.py +43 -0
- agent_api_server-2.2.1a2/adapters/langgraph_adapter/__init__.py +53 -0
- agent_api_server-2.2.1a2/adapters/langgraph_adapter/formatter.py +225 -0
- agent_api_server-2.2.1a2/adapters/langgraph_adapter/langgraph_adapter.py +121 -0
- agent_api_server-2.2.1a2/adapters/openclaw_adapter/__init__.py +26 -0
- agent_api_server-2.2.1a2/adapters/openclaw_adapter/openclaw_adapter.py +893 -0
- agent_api_server-2.2.1a2/agent_api_server.py +12 -0
- agent_api_server-2.2.1a2/api/__init__.py +3 -0
- {agent_api_server-2.2.1/agent_api_server/api/v1 → agent_api_server-2.2.1a2/api}/config.py +5 -17
- {agent_api_server-2.2.1/agent_api_server/api/v1 → agent_api_server-2.2.1a2/api}/graph.py +3 -3
- agent_api_server-2.2.1/agent_api_server/api/v1/api.py → agent_api_server-2.2.1a2/api/router.py +5 -10
- {agent_api_server-2.2.1/agent_api_server/api/v1 → agent_api_server-2.2.1a2/api}/schema.py +6 -30
- agent_api_server-2.2.1a2/api/thread.py +577 -0
- agent_api_server-2.2.1a2/client/css/styles.css +561 -0
- agent_api_server-2.2.1a2/client/index.html +106 -0
- agent_api_server-2.2.1a2/client/js/app.js +892 -0
- agent_api_server-2.2.1a2/common/__init__.py +25 -0
- agent_api_server-2.2.1a2/common/config.py +106 -0
- agent_api_server-2.2.1a2/common/formatting.py +46 -0
- agent_api_server-2.2.1a2/common/logging.py +126 -0
- agent_api_server-2.2.1a2/common/nats.py +103 -0
- {agent_api_server-2.2.1/agent_api_server/memeory → agent_api_server-2.2.1a2/common}/postgres.py +27 -92
- agent_api_server-2.2.1/agent_api_server/cache/redis_cache.py → agent_api_server-2.2.1a2/common/redis.py +71 -148
- agent_api_server-2.2.1a2/core/__init__.py +57 -0
- agent_api_server-2.2.1a2/core/loader.py +203 -0
- agent_api_server-2.2.1a2/core/model/__init__.py +35 -0
- agent_api_server-2.2.1a2/core/model/agent_models.py +121 -0
- {agent_api_server-2.2.1/agent_api_server/dynamic_llm → agent_api_server-2.2.1a2/core/model}/dynamic_llm.py +52 -9
- agent_api_server-2.2.1a2/core/model/input_normalization.py +50 -0
- agent_api_server-2.2.1/agent_api_server/middleware/model.py → agent_api_server-2.2.1a2/core/model/middleware.py +11 -6
- agent_api_server-2.2.1/agent_api_server/shared/get_model_info.py → agent_api_server-2.2.1a2/core/model/model_info.py +11 -5
- agent_api_server-2.2.1a2/core/model/streaming.py +89 -0
- agent_api_server-2.2.1a2/core/runtime/__init__.py +26 -0
- agent_api_server-2.2.1a2/core/runtime/base.py +98 -0
- agent_api_server-2.2.1a2/core/runtime/manager.py +180 -0
- agent_api_server-2.2.1a2/demo.py +25 -0
- agent_api_server-2.2.1a2/integration/__init__.py +10 -0
- {agent_api_server-2.2.1/agent_api_server → agent_api_server-2.2.1a2/integration}/listener.py +43 -39
- agent_api_server-2.2.1/agent_api_server/register/register.py → agent_api_server-2.2.1a2/integration/registry.py +4 -7
- {agent_api_server-2.2.1/agent_api_server/log → agent_api_server-2.2.1a2}/logging.json +2 -2
- {agent_api_server-2.2.1 → agent_api_server-2.2.1a2}/pyproject.toml +17 -22
- agent_api_server-2.2.1a2/sdk/__init__.py +3 -0
- agent_api_server-2.2.1a2/sdk/client.py +326 -0
- agent_api_server-2.2.1a2/service.py +198 -0
- agent_api_server-2.2.1/PKG-INFO +0 -110
- agent_api_server-2.2.1/README.md +0 -70
- agent_api_server-2.2.1/agent_api_server/__init__.py +0 -0
- agent_api_server-2.2.1/agent_api_server/api/__init__.py +0 -0
- agent_api_server-2.2.1/agent_api_server/api/v1/__init__.py +0 -0
- agent_api_server-2.2.1/agent_api_server/api/v1/thread.py +0 -562
- agent_api_server-2.2.1/agent_api_server/cache/__init__.py +0 -0
- agent_api_server-2.2.1/agent_api_server/callback_handler.py +0 -18
- agent_api_server-2.2.1/agent_api_server/client/css/styles.css +0 -1202
- agent_api_server-2.2.1/agent_api_server/client/index.html +0 -102
- agent_api_server-2.2.1/agent_api_server/client/js/app.js +0 -1499
- agent_api_server-2.2.1/agent_api_server/config_center/config_center.py +0 -239
- agent_api_server-2.2.1/agent_api_server/configs/__init__.py +0 -3
- agent_api_server-2.2.1/agent_api_server/configs/config.py +0 -163
- agent_api_server-2.2.1/agent_api_server/dynamic_llm/__init__.py +0 -0
- agent_api_server-2.2.1/agent_api_server/dynamic_llm/model.py +0 -43
- agent_api_server-2.2.1/agent_api_server/log/__init__.py +0 -0
- agent_api_server-2.2.1/agent_api_server/log/formatters.py +0 -122
- agent_api_server-2.2.1/agent_api_server/mcp_convert/__init__.py +0 -0
- agent_api_server-2.2.1/agent_api_server/mcp_convert/mcp_convert.py +0 -367
- agent_api_server-2.2.1/agent_api_server/mcp_interceptor/__init__.py +0 -0
- agent_api_server-2.2.1/agent_api_server/mcp_interceptor/mcp_intecerpter.py +0 -20
- agent_api_server-2.2.1/agent_api_server/memeory/__init__.py +0 -0
- agent_api_server-2.2.1/agent_api_server/middleware/__init__.py +0 -0
- agent_api_server-2.2.1/agent_api_server/middleware/schema.py +0 -6
- agent_api_server-2.2.1/agent_api_server/register/__init__.py +0 -0
- agent_api_server-2.2.1/agent_api_server/schema/__init__.py +0 -0
- agent_api_server-2.2.1/agent_api_server/schema/context.py +0 -13
- agent_api_server-2.2.1/agent_api_server/service.py +0 -354
- agent_api_server-2.2.1/agent_api_server/service_hub/service_hub.py +0 -233
- agent_api_server-2.2.1/agent_api_server/service_hub/service_hub_test.py +0 -700
- agent_api_server-2.2.1/agent_api_server/shared/__init__.py +0 -0
- agent_api_server-2.2.1/agent_api_server/shared/message.py +0 -383
- agent_api_server-2.2.1/agent_api_server/shared/util_func.py +0 -372
- agent_api_server-2.2.1/agent_api_server/sso_service/__init__.py +0 -1
- agent_api_server-2.2.1/agent_api_server/sso_service/sdk/__init__.py +0 -1
- agent_api_server-2.2.1/agent_api_server/sso_service/sdk/client.py +0 -224
- agent_api_server-2.2.1/agent_api_server/sso_service/sdk/credential.py +0 -11
- agent_api_server-2.2.1/agent_api_server/sso_service/sdk/encoding.py +0 -22
- agent_api_server-2.2.1/agent_api_server/sso_service/sso_service.py +0 -177
- {agent_api_server-2.2.1/agent_api_server → agent_api_server-2.2.1a2}/client/favicon.ico +0 -0
- {agent_api_server-2.2.1/agent_api_server → agent_api_server-2.2.1a2}/client/js/index.umd.js +0 -0
- /agent_api_server-2.2.1/agent_api_server/shared/ase.py → /agent_api_server-2.2.1a2/common/crypto.py +0 -0
- {agent_api_server-2.2.1/agent_api_server/shared → agent_api_server-2.2.1a2/core/model}/base_model.py +0 -0
- {agent_api_server-2.2.1/agent_api_server/shared → agent_api_server-2.2.1a2/core/model}/detect_message.py +0 -0
- /agent_api_server-2.2.1/agent_api_server/shared/common.py → /agent_api_server-2.2.1a2/core/model/schema_utils.py +0 -0
|
@@ -0,0 +1,308 @@
|
|
|
1
|
+
Metadata-Version: 2.3
|
|
2
|
+
Name: agent-api-server
|
|
3
|
+
Version: 2.2.1a2
|
|
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
|
+
Requires-Python: >=3.11,<3.14
|
|
7
|
+
Classifier: Development Status :: 4 - Beta
|
|
8
|
+
Classifier: Framework :: FastAPI
|
|
9
|
+
Classifier: Programming Language :: Python :: 3
|
|
10
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
11
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
12
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
13
|
+
Requires-Dist: aiofiles (>=24.1.0,<25.0.0)
|
|
14
|
+
Requires-Dist: cryptography (>=45.0.4,<46.0.0)
|
|
15
|
+
Requires-Dist: fastapi (>=0.117.0,<0.121.2)
|
|
16
|
+
Requires-Dist: langchain (>=1.2.0,<2.0.0)
|
|
17
|
+
Requires-Dist: langchain-core (>=1.2.5,<2.0.0)
|
|
18
|
+
Requires-Dist: langgraph (>=1.0.6,<2.0.0)
|
|
19
|
+
Requires-Dist: langgraph-checkpoint (>=4.0.0,<5.0.0)
|
|
20
|
+
Requires-Dist: langgraph-checkpoint-postgres (>=3.0.3,<4.0.0)
|
|
21
|
+
Requires-Dist: llm-sdk (==1.0.1)
|
|
22
|
+
Requires-Dist: model-manage-client (>=0.0.1.8)
|
|
23
|
+
Requires-Dist: nats-py (>=2.11.0,<3.0.0)
|
|
24
|
+
Requires-Dist: openclaw-sdk (>=2.1.0,<3.0.0)
|
|
25
|
+
Requires-Dist: psycopg-binary (>=3.2.9,<4.0.0)
|
|
26
|
+
Requires-Dist: psycopg-pool (>=3.2.6,<4.0.0)
|
|
27
|
+
Requires-Dist: pydantic-settings (>=2.9.1,<3.0.0)
|
|
28
|
+
Requires-Dist: redis (>=6.2.0,<7.0.0)
|
|
29
|
+
Requires-Dist: starlette (>=0.49.3,<0.50.0)
|
|
30
|
+
Requires-Dist: tenacity (>=9.1.2,<10.0.0)
|
|
31
|
+
Description-Content-Type: text/markdown
|
|
32
|
+
|
|
33
|
+
# agent-api-server
|
|
34
|
+
|
|
35
|
+
`agent-api-server` is an agent runtime that now supports both:
|
|
36
|
+
|
|
37
|
+
- API Server mode for HTTP/SSE access
|
|
38
|
+
- SDK mode for direct in-process agent execution
|
|
39
|
+
|
|
40
|
+
It keeps compatibility with existing LangGraph-based agents and introduces a framework abstraction so additional runtimes such as OpenClaw can be added behind the same interface.
|
|
41
|
+
|
|
42
|
+
## Features
|
|
43
|
+
|
|
44
|
+
- FastAPI application factory for embedding or standalone deployment
|
|
45
|
+
- Direct SDK entry via root module `agent_api_server.py`
|
|
46
|
+
- Framework-neutral agent loading with LangGraph and OpenClaw adapters
|
|
47
|
+
- Backward-compatible 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 model management registration and listener services
|
|
51
|
+
- Layered package structure centered on `adapters/`, `core/`, `common/`, `api/`, `integration/`, and `sdk/`
|
|
52
|
+
|
|
53
|
+
## Requirements
|
|
54
|
+
|
|
55
|
+
- Python 3.11 to 3.13
|
|
56
|
+
- Redis
|
|
57
|
+
- PostgreSQL
|
|
58
|
+
- Access to all runtime dependencies declared in `pyproject.toml`
|
|
59
|
+
|
|
60
|
+
## Installation
|
|
61
|
+
|
|
62
|
+
Install from a package index that provides all required dependencies:
|
|
63
|
+
|
|
64
|
+
```bash
|
|
65
|
+
pip install agent-api-server
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
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.
|
|
69
|
+
|
|
70
|
+
## Configuration
|
|
71
|
+
|
|
72
|
+
The runtime reads configuration from environment variables. Common settings include:
|
|
73
|
+
|
|
74
|
+
```env
|
|
75
|
+
REDIS_URL=redis://localhost:6379/0
|
|
76
|
+
POSTGRES_URL=postgresql://postgres:postgres@localhost:5432/postgres
|
|
77
|
+
MODEL_MANAGER_SERVICE_URL=http://127.0.0.1:10053
|
|
78
|
+
CLIENT_TOKEN=
|
|
79
|
+
SERVER_PORT=8080
|
|
80
|
+
SERVER_WORKER_AMOUNT=1
|
|
81
|
+
LOG_LEVEL=INFO
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
See `.env_example` for a more complete example.
|
|
85
|
+
|
|
86
|
+
## Agent Config
|
|
87
|
+
|
|
88
|
+
`agents.json` is now the primary agent registry. Existing deployments that still use `langgraph.json` remain compatible via fallback loading. LangGraph string entries still work:
|
|
89
|
+
|
|
90
|
+
```json
|
|
91
|
+
{
|
|
92
|
+
"graphs": {
|
|
93
|
+
"demo-agent": "./agents/demo.py:graph"
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
You can also use the extended form to declare the framework explicitly and attach adapter-specific settings:
|
|
99
|
+
|
|
100
|
+
```json
|
|
101
|
+
{
|
|
102
|
+
"graphs": {
|
|
103
|
+
"demo-agent": {
|
|
104
|
+
"framework": "langgraph",
|
|
105
|
+
"entrypoint": "./agents/demo.py:graph"
|
|
106
|
+
},
|
|
107
|
+
"openclaw-agent": {
|
|
108
|
+
"framework": "openclaw",
|
|
109
|
+
"agent_id": "openclaw-agent",
|
|
110
|
+
"create_agent": true,
|
|
111
|
+
"agent_config": {
|
|
112
|
+
"name": "OpenClaw Agent"
|
|
113
|
+
},
|
|
114
|
+
"client": {
|
|
115
|
+
"gateway_ws_url": "ws://127.0.0.1:18789/gateway",
|
|
116
|
+
"api_key": "your_gateway_token",
|
|
117
|
+
"scopes": ["operator.read", "operator.write"],
|
|
118
|
+
"device_identity_path": "~/.openclaw/identity/device.json",
|
|
119
|
+
"timeout": 60
|
|
120
|
+
},
|
|
121
|
+
"input_schema": {
|
|
122
|
+
"type": "object",
|
|
123
|
+
"properties": {
|
|
124
|
+
"query": {
|
|
125
|
+
"type": "string"
|
|
126
|
+
}
|
|
127
|
+
},
|
|
128
|
+
"required": ["query"]
|
|
129
|
+
},
|
|
130
|
+
"context_schema": {
|
|
131
|
+
"type": "object",
|
|
132
|
+
"properties": {
|
|
133
|
+
"CHAT_PROVIDER": {
|
|
134
|
+
"type": "string"
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
```
|
|
142
|
+
|
|
143
|
+
For OpenClaw agents, API and SDK calls map the local `thread_id` to the OpenClaw SDK `session_name`, so each Chatbot conversation uses an isolated OpenClaw session by default. When `create_agent` is enabled, thread creation calls `list_agents()` and creates the remote OpenClaw agent if it is missing. If `workspace` is omitted, the adapter creates the agent under the OpenClaw client work directory using the OpenClaw workspace naming convention, for example `.openclaw/workspace-openclaw-agent`.
|
|
144
|
+
|
|
145
|
+
The Gateway protocol requires both `auth.token` and a signed `device` identity during `connect`. In practice that means `client.api_key` alone is not enough for a normal WS connection: you also need a paired device identity (default `~/.openclaw/identity/device.json`) or a gateway configured for insecure local auth.
|
|
146
|
+
|
|
147
|
+
When `client.api_key` is configured, this project now auto-generates a local Ed25519 device identity if `client.device_identity_path` does not exist yet. The first connection may still require device approval on the gateway before `hello-ok.auth.deviceToken` is issued.
|
|
148
|
+
|
|
149
|
+
## Request Payloads
|
|
150
|
+
|
|
151
|
+
Thread `run` and `stream` endpoints accept the current payload shape:
|
|
152
|
+
|
|
153
|
+
```json
|
|
154
|
+
{
|
|
155
|
+
"query": "hello",
|
|
156
|
+
"attachments": []
|
|
157
|
+
}
|
|
158
|
+
```
|
|
159
|
+
|
|
160
|
+
They also accept the legacy Chatbot payload shape:
|
|
161
|
+
|
|
162
|
+
```json
|
|
163
|
+
{
|
|
164
|
+
"inputs": {
|
|
165
|
+
"user_input": "hello"
|
|
166
|
+
},
|
|
167
|
+
"attachments": []
|
|
168
|
+
}
|
|
169
|
+
```
|
|
170
|
+
|
|
171
|
+
Internally both forms are normalized to `{"query": "hello"}` plus an attachment list before they reach the adapter. OpenClaw SSE events include `conversation_id` and use `model` / `tools` nodes for `node_message`, `tools_message`, and `token_stream` events.
|
|
172
|
+
|
|
173
|
+
## Running the server
|
|
174
|
+
|
|
175
|
+
Run the application with Uvicorn:
|
|
176
|
+
|
|
177
|
+
```bash
|
|
178
|
+
uvicorn service:create_fastapi_app --factory --host 0.0.0.0 --port 8080
|
|
179
|
+
```
|
|
180
|
+
|
|
181
|
+
After startup:
|
|
182
|
+
|
|
183
|
+
- API root: `http://127.0.0.1:8080/api/v1`
|
|
184
|
+
- OpenAPI docs: `http://127.0.0.1:8080/docs`
|
|
185
|
+
- Built-in client: `http://127.0.0.1:8080/site`
|
|
186
|
+
|
|
187
|
+
## SDK Usage
|
|
188
|
+
|
|
189
|
+
For OpenClaw agents, a package consumer can configure the gateway directly in the SDK constructor instead of shipping an `agents.json` file:
|
|
190
|
+
|
|
191
|
+
```python
|
|
192
|
+
import asyncio
|
|
193
|
+
|
|
194
|
+
from agent_api_server import AgentSDK
|
|
195
|
+
|
|
196
|
+
|
|
197
|
+
async def main():
|
|
198
|
+
sdk = AgentSDK(
|
|
199
|
+
agent_name="demo-openclaw-agent",
|
|
200
|
+
agent_id="your-openclaw-agent-id",
|
|
201
|
+
gateway_ws_url="ws://127.0.0.1:18789/gateway",
|
|
202
|
+
api_key="your_gateway_token",
|
|
203
|
+
scopes=["operator.read", "operator.write"],
|
|
204
|
+
device_identity_path="~/.openclaw/identity/device.json",
|
|
205
|
+
timeout=60,
|
|
206
|
+
)
|
|
207
|
+
result = await sdk.run(query="hello", thread_id="sdk-run-thread")
|
|
208
|
+
print(result.content)
|
|
209
|
+
|
|
210
|
+
|
|
211
|
+
asyncio.run(main())
|
|
212
|
+
```
|
|
213
|
+
|
|
214
|
+
To print each streamed chunk to the console as it arrives:
|
|
215
|
+
|
|
216
|
+
```python
|
|
217
|
+
import asyncio
|
|
218
|
+
|
|
219
|
+
from agent_api_server import AgentSDK
|
|
220
|
+
|
|
221
|
+
|
|
222
|
+
async def main():
|
|
223
|
+
sdk = AgentSDK(
|
|
224
|
+
agent_name="demo-openclaw-agent",
|
|
225
|
+
agent_id="your-openclaw-agent-id",
|
|
226
|
+
gateway_ws_url="ws://127.0.0.1:18789/gateway",
|
|
227
|
+
api_key="your_gateway_token",
|
|
228
|
+
scopes=["operator.read", "operator.write"],
|
|
229
|
+
device_identity_path="~/.openclaw/identity/device.json",
|
|
230
|
+
timeout=60,
|
|
231
|
+
)
|
|
232
|
+
async for chunk in sdk.stream(query="hello", thread_id="sdk-stream-thread"):
|
|
233
|
+
print(chunk, end="", flush=True)
|
|
234
|
+
|
|
235
|
+
|
|
236
|
+
asyncio.run(main())
|
|
237
|
+
```
|
|
238
|
+
|
|
239
|
+
The existing config-file style remains supported:
|
|
240
|
+
|
|
241
|
+
```python
|
|
242
|
+
import asyncio
|
|
243
|
+
|
|
244
|
+
from agent_api_server import AgentSDK
|
|
245
|
+
|
|
246
|
+
|
|
247
|
+
async def main():
|
|
248
|
+
sdk = AgentSDK()
|
|
249
|
+
result = await sdk.run(
|
|
250
|
+
"demo-agent",
|
|
251
|
+
{"query": "hello"},
|
|
252
|
+
thread_id="sdk-thread",
|
|
253
|
+
)
|
|
254
|
+
print(result.content)
|
|
255
|
+
|
|
256
|
+
|
|
257
|
+
asyncio.run(main())
|
|
258
|
+
```
|
|
259
|
+
|
|
260
|
+
For synchronous usage with direct OpenClaw settings:
|
|
261
|
+
|
|
262
|
+
```python
|
|
263
|
+
from agent_api_server import AgentSDK
|
|
264
|
+
|
|
265
|
+
sdk = AgentSDK(
|
|
266
|
+
agent_name="demo-openclaw-agent",
|
|
267
|
+
agent_id="your-openclaw-agent-id",
|
|
268
|
+
gateway_ws_url="ws://127.0.0.1:18789/gateway",
|
|
269
|
+
api_key="your_gateway_token",
|
|
270
|
+
scopes=["operator.read", "operator.write"],
|
|
271
|
+
device_identity_path="~/.openclaw/identity/device.json",
|
|
272
|
+
timeout=60,
|
|
273
|
+
)
|
|
274
|
+
result = sdk.run_sync(query="hello", thread_id="sdk-sync-thread")
|
|
275
|
+
print(result.content)
|
|
276
|
+
```
|
|
277
|
+
|
|
278
|
+
For synchronous usage with config-file-managed agents:
|
|
279
|
+
|
|
280
|
+
```python
|
|
281
|
+
from agent_api_server import AgentSDK
|
|
282
|
+
|
|
283
|
+
sdk = AgentSDK()
|
|
284
|
+
result = sdk.run_sync("demo-agent", {"query": "hello"}, thread_id="sync-thread")
|
|
285
|
+
print(result.content)
|
|
286
|
+
```
|
|
287
|
+
|
|
288
|
+
`attachments` is the public file/URL metadata channel. `ts_tenant`, `ei_token`, `runtime_config`, and `use_system_llm` are optional advanced parameters for deployments that need tenant-specific model credentials or LangGraph runtime configuration.
|
|
289
|
+
|
|
290
|
+
## Build
|
|
291
|
+
|
|
292
|
+
Build source and wheel distributions with Poetry:
|
|
293
|
+
|
|
294
|
+
```bash
|
|
295
|
+
poetry build
|
|
296
|
+
```
|
|
297
|
+
|
|
298
|
+
## Layout
|
|
299
|
+
|
|
300
|
+
The repository is now organized as layered packages:
|
|
301
|
+
|
|
302
|
+
- `adapters/` for framework-specific agent adapters
|
|
303
|
+
- `core/` for agent runtime, loading, and shared execution models
|
|
304
|
+
- `common/` for config, logging, crypto, Redis, Postgres, NATS, and formatting helpers
|
|
305
|
+
- `api/` for FastAPI route modules
|
|
306
|
+
- `integration/` for registration and model-update listener integrations
|
|
307
|
+
- `sdk/` for the direct SDK client
|
|
308
|
+
|
|
@@ -0,0 +1,275 @@
|
|
|
1
|
+
# agent-api-server
|
|
2
|
+
|
|
3
|
+
`agent-api-server` is an agent runtime that now supports both:
|
|
4
|
+
|
|
5
|
+
- API Server mode for HTTP/SSE access
|
|
6
|
+
- SDK mode for direct in-process agent execution
|
|
7
|
+
|
|
8
|
+
It keeps compatibility with existing LangGraph-based agents and introduces a framework abstraction so additional runtimes such as OpenClaw can be added behind the same interface.
|
|
9
|
+
|
|
10
|
+
## Features
|
|
11
|
+
|
|
12
|
+
- FastAPI application factory for embedding or standalone deployment
|
|
13
|
+
- Direct SDK entry via root module `agent_api_server.py`
|
|
14
|
+
- Framework-neutral agent loading with LangGraph and OpenClaw adapters
|
|
15
|
+
- Backward-compatible LangGraph-oriented API routes under `/api/v1`
|
|
16
|
+
- Built-in static client assets bundled in both sdist and wheel artifacts
|
|
17
|
+
- Redis-backed thread storage and PostgreSQL checkpoint integration
|
|
18
|
+
- Optional integration with model management registration and listener services
|
|
19
|
+
- Layered package structure centered on `adapters/`, `core/`, `common/`, `api/`, `integration/`, and `sdk/`
|
|
20
|
+
|
|
21
|
+
## Requirements
|
|
22
|
+
|
|
23
|
+
- Python 3.11 to 3.13
|
|
24
|
+
- Redis
|
|
25
|
+
- PostgreSQL
|
|
26
|
+
- Access to all runtime dependencies declared in `pyproject.toml`
|
|
27
|
+
|
|
28
|
+
## Installation
|
|
29
|
+
|
|
30
|
+
Install from a package index that provides all required dependencies:
|
|
31
|
+
|
|
32
|
+
```bash
|
|
33
|
+
pip install agent-api-server
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
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.
|
|
37
|
+
|
|
38
|
+
## Configuration
|
|
39
|
+
|
|
40
|
+
The runtime reads configuration from environment variables. Common settings include:
|
|
41
|
+
|
|
42
|
+
```env
|
|
43
|
+
REDIS_URL=redis://localhost:6379/0
|
|
44
|
+
POSTGRES_URL=postgresql://postgres:postgres@localhost:5432/postgres
|
|
45
|
+
MODEL_MANAGER_SERVICE_URL=http://127.0.0.1:10053
|
|
46
|
+
CLIENT_TOKEN=
|
|
47
|
+
SERVER_PORT=8080
|
|
48
|
+
SERVER_WORKER_AMOUNT=1
|
|
49
|
+
LOG_LEVEL=INFO
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
See `.env_example` for a more complete example.
|
|
53
|
+
|
|
54
|
+
## Agent Config
|
|
55
|
+
|
|
56
|
+
`agents.json` is now the primary agent registry. Existing deployments that still use `langgraph.json` remain compatible via fallback loading. LangGraph string entries still work:
|
|
57
|
+
|
|
58
|
+
```json
|
|
59
|
+
{
|
|
60
|
+
"graphs": {
|
|
61
|
+
"demo-agent": "./agents/demo.py:graph"
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
You can also use the extended form to declare the framework explicitly and attach adapter-specific settings:
|
|
67
|
+
|
|
68
|
+
```json
|
|
69
|
+
{
|
|
70
|
+
"graphs": {
|
|
71
|
+
"demo-agent": {
|
|
72
|
+
"framework": "langgraph",
|
|
73
|
+
"entrypoint": "./agents/demo.py:graph"
|
|
74
|
+
},
|
|
75
|
+
"openclaw-agent": {
|
|
76
|
+
"framework": "openclaw",
|
|
77
|
+
"agent_id": "openclaw-agent",
|
|
78
|
+
"create_agent": true,
|
|
79
|
+
"agent_config": {
|
|
80
|
+
"name": "OpenClaw Agent"
|
|
81
|
+
},
|
|
82
|
+
"client": {
|
|
83
|
+
"gateway_ws_url": "ws://127.0.0.1:18789/gateway",
|
|
84
|
+
"api_key": "your_gateway_token",
|
|
85
|
+
"scopes": ["operator.read", "operator.write"],
|
|
86
|
+
"device_identity_path": "~/.openclaw/identity/device.json",
|
|
87
|
+
"timeout": 60
|
|
88
|
+
},
|
|
89
|
+
"input_schema": {
|
|
90
|
+
"type": "object",
|
|
91
|
+
"properties": {
|
|
92
|
+
"query": {
|
|
93
|
+
"type": "string"
|
|
94
|
+
}
|
|
95
|
+
},
|
|
96
|
+
"required": ["query"]
|
|
97
|
+
},
|
|
98
|
+
"context_schema": {
|
|
99
|
+
"type": "object",
|
|
100
|
+
"properties": {
|
|
101
|
+
"CHAT_PROVIDER": {
|
|
102
|
+
"type": "string"
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
For OpenClaw agents, API and SDK calls map the local `thread_id` to the OpenClaw SDK `session_name`, so each Chatbot conversation uses an isolated OpenClaw session by default. When `create_agent` is enabled, thread creation calls `list_agents()` and creates the remote OpenClaw agent if it is missing. If `workspace` is omitted, the adapter creates the agent under the OpenClaw client work directory using the OpenClaw workspace naming convention, for example `.openclaw/workspace-openclaw-agent`.
|
|
112
|
+
|
|
113
|
+
The Gateway protocol requires both `auth.token` and a signed `device` identity during `connect`. In practice that means `client.api_key` alone is not enough for a normal WS connection: you also need a paired device identity (default `~/.openclaw/identity/device.json`) or a gateway configured for insecure local auth.
|
|
114
|
+
|
|
115
|
+
When `client.api_key` is configured, this project now auto-generates a local Ed25519 device identity if `client.device_identity_path` does not exist yet. The first connection may still require device approval on the gateway before `hello-ok.auth.deviceToken` is issued.
|
|
116
|
+
|
|
117
|
+
## Request Payloads
|
|
118
|
+
|
|
119
|
+
Thread `run` and `stream` endpoints accept the current payload shape:
|
|
120
|
+
|
|
121
|
+
```json
|
|
122
|
+
{
|
|
123
|
+
"query": "hello",
|
|
124
|
+
"attachments": []
|
|
125
|
+
}
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
They also accept the legacy Chatbot payload shape:
|
|
129
|
+
|
|
130
|
+
```json
|
|
131
|
+
{
|
|
132
|
+
"inputs": {
|
|
133
|
+
"user_input": "hello"
|
|
134
|
+
},
|
|
135
|
+
"attachments": []
|
|
136
|
+
}
|
|
137
|
+
```
|
|
138
|
+
|
|
139
|
+
Internally both forms are normalized to `{"query": "hello"}` plus an attachment list before they reach the adapter. OpenClaw SSE events include `conversation_id` and use `model` / `tools` nodes for `node_message`, `tools_message`, and `token_stream` events.
|
|
140
|
+
|
|
141
|
+
## Running the server
|
|
142
|
+
|
|
143
|
+
Run the application with Uvicorn:
|
|
144
|
+
|
|
145
|
+
```bash
|
|
146
|
+
uvicorn service:create_fastapi_app --factory --host 0.0.0.0 --port 8080
|
|
147
|
+
```
|
|
148
|
+
|
|
149
|
+
After startup:
|
|
150
|
+
|
|
151
|
+
- API root: `http://127.0.0.1:8080/api/v1`
|
|
152
|
+
- OpenAPI docs: `http://127.0.0.1:8080/docs`
|
|
153
|
+
- Built-in client: `http://127.0.0.1:8080/site`
|
|
154
|
+
|
|
155
|
+
## SDK Usage
|
|
156
|
+
|
|
157
|
+
For OpenClaw agents, a package consumer can configure the gateway directly in the SDK constructor instead of shipping an `agents.json` file:
|
|
158
|
+
|
|
159
|
+
```python
|
|
160
|
+
import asyncio
|
|
161
|
+
|
|
162
|
+
from agent_api_server import AgentSDK
|
|
163
|
+
|
|
164
|
+
|
|
165
|
+
async def main():
|
|
166
|
+
sdk = AgentSDK(
|
|
167
|
+
agent_name="demo-openclaw-agent",
|
|
168
|
+
agent_id="your-openclaw-agent-id",
|
|
169
|
+
gateway_ws_url="ws://127.0.0.1:18789/gateway",
|
|
170
|
+
api_key="your_gateway_token",
|
|
171
|
+
scopes=["operator.read", "operator.write"],
|
|
172
|
+
device_identity_path="~/.openclaw/identity/device.json",
|
|
173
|
+
timeout=60,
|
|
174
|
+
)
|
|
175
|
+
result = await sdk.run(query="hello", thread_id="sdk-run-thread")
|
|
176
|
+
print(result.content)
|
|
177
|
+
|
|
178
|
+
|
|
179
|
+
asyncio.run(main())
|
|
180
|
+
```
|
|
181
|
+
|
|
182
|
+
To print each streamed chunk to the console as it arrives:
|
|
183
|
+
|
|
184
|
+
```python
|
|
185
|
+
import asyncio
|
|
186
|
+
|
|
187
|
+
from agent_api_server import AgentSDK
|
|
188
|
+
|
|
189
|
+
|
|
190
|
+
async def main():
|
|
191
|
+
sdk = AgentSDK(
|
|
192
|
+
agent_name="demo-openclaw-agent",
|
|
193
|
+
agent_id="your-openclaw-agent-id",
|
|
194
|
+
gateway_ws_url="ws://127.0.0.1:18789/gateway",
|
|
195
|
+
api_key="your_gateway_token",
|
|
196
|
+
scopes=["operator.read", "operator.write"],
|
|
197
|
+
device_identity_path="~/.openclaw/identity/device.json",
|
|
198
|
+
timeout=60,
|
|
199
|
+
)
|
|
200
|
+
async for chunk in sdk.stream(query="hello", thread_id="sdk-stream-thread"):
|
|
201
|
+
print(chunk, end="", flush=True)
|
|
202
|
+
|
|
203
|
+
|
|
204
|
+
asyncio.run(main())
|
|
205
|
+
```
|
|
206
|
+
|
|
207
|
+
The existing config-file style remains supported:
|
|
208
|
+
|
|
209
|
+
```python
|
|
210
|
+
import asyncio
|
|
211
|
+
|
|
212
|
+
from agent_api_server import AgentSDK
|
|
213
|
+
|
|
214
|
+
|
|
215
|
+
async def main():
|
|
216
|
+
sdk = AgentSDK()
|
|
217
|
+
result = await sdk.run(
|
|
218
|
+
"demo-agent",
|
|
219
|
+
{"query": "hello"},
|
|
220
|
+
thread_id="sdk-thread",
|
|
221
|
+
)
|
|
222
|
+
print(result.content)
|
|
223
|
+
|
|
224
|
+
|
|
225
|
+
asyncio.run(main())
|
|
226
|
+
```
|
|
227
|
+
|
|
228
|
+
For synchronous usage with direct OpenClaw settings:
|
|
229
|
+
|
|
230
|
+
```python
|
|
231
|
+
from agent_api_server import AgentSDK
|
|
232
|
+
|
|
233
|
+
sdk = AgentSDK(
|
|
234
|
+
agent_name="demo-openclaw-agent",
|
|
235
|
+
agent_id="your-openclaw-agent-id",
|
|
236
|
+
gateway_ws_url="ws://127.0.0.1:18789/gateway",
|
|
237
|
+
api_key="your_gateway_token",
|
|
238
|
+
scopes=["operator.read", "operator.write"],
|
|
239
|
+
device_identity_path="~/.openclaw/identity/device.json",
|
|
240
|
+
timeout=60,
|
|
241
|
+
)
|
|
242
|
+
result = sdk.run_sync(query="hello", thread_id="sdk-sync-thread")
|
|
243
|
+
print(result.content)
|
|
244
|
+
```
|
|
245
|
+
|
|
246
|
+
For synchronous usage with config-file-managed agents:
|
|
247
|
+
|
|
248
|
+
```python
|
|
249
|
+
from agent_api_server import AgentSDK
|
|
250
|
+
|
|
251
|
+
sdk = AgentSDK()
|
|
252
|
+
result = sdk.run_sync("demo-agent", {"query": "hello"}, thread_id="sync-thread")
|
|
253
|
+
print(result.content)
|
|
254
|
+
```
|
|
255
|
+
|
|
256
|
+
`attachments` is the public file/URL metadata channel. `ts_tenant`, `ei_token`, `runtime_config`, and `use_system_llm` are optional advanced parameters for deployments that need tenant-specific model credentials or LangGraph runtime configuration.
|
|
257
|
+
|
|
258
|
+
## Build
|
|
259
|
+
|
|
260
|
+
Build source and wheel distributions with Poetry:
|
|
261
|
+
|
|
262
|
+
```bash
|
|
263
|
+
poetry build
|
|
264
|
+
```
|
|
265
|
+
|
|
266
|
+
## Layout
|
|
267
|
+
|
|
268
|
+
The repository is now organized as layered packages:
|
|
269
|
+
|
|
270
|
+
- `adapters/` for framework-specific agent adapters
|
|
271
|
+
- `core/` for agent runtime, loading, and shared execution models
|
|
272
|
+
- `common/` for config, logging, crypto, Redis, Postgres, NATS, and formatting helpers
|
|
273
|
+
- `api/` for FastAPI route modules
|
|
274
|
+
- `integration/` for registration and model-update listener integrations
|
|
275
|
+
- `sdk/` for the direct SDK client
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
from importlib import import_module
|
|
2
|
+
from typing import TYPE_CHECKING
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
_EXPORTS = {
|
|
6
|
+
"LangGraphAgentAdapter": (
|
|
7
|
+
"adapters.langgraph_adapter.langgraph_adapter",
|
|
8
|
+
"LangGraphAgentAdapter",
|
|
9
|
+
),
|
|
10
|
+
"OpenClawAgentAdapter": (
|
|
11
|
+
"adapters.openclaw_adapter.openclaw_adapter",
|
|
12
|
+
"OpenClawAgentAdapter",
|
|
13
|
+
),
|
|
14
|
+
"handle_stream_event": (
|
|
15
|
+
"adapters.langgraph_adapter.formatter",
|
|
16
|
+
"handle_stream_event",
|
|
17
|
+
),
|
|
18
|
+
"langchain_to_chat_message": (
|
|
19
|
+
"adapters.langgraph_adapter.formatter",
|
|
20
|
+
"langchain_to_chat_message",
|
|
21
|
+
),
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
__all__ = list(_EXPORTS)
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
def __getattr__(name: str):
|
|
28
|
+
if name not in _EXPORTS:
|
|
29
|
+
raise AttributeError(f"module 'adapters' has no attribute '{name}'")
|
|
30
|
+
module_name, attr_name = _EXPORTS[name]
|
|
31
|
+
module = import_module(module_name)
|
|
32
|
+
value = getattr(module, attr_name)
|
|
33
|
+
globals()[name] = value
|
|
34
|
+
return value
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
if TYPE_CHECKING:
|
|
38
|
+
from adapters.langgraph_adapter.formatter import (
|
|
39
|
+
handle_stream_event,
|
|
40
|
+
langchain_to_chat_message,
|
|
41
|
+
)
|
|
42
|
+
from adapters.langgraph_adapter.langgraph_adapter import LangGraphAgentAdapter
|
|
43
|
+
from adapters.openclaw_adapter.openclaw_adapter import OpenClawAgentAdapter
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
from importlib import import_module
|
|
2
|
+
from typing import TYPE_CHECKING
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
_EXPORTS = {
|
|
6
|
+
"LangGraphAgentAdapter": (
|
|
7
|
+
"adapters.langgraph_adapter.langgraph_adapter",
|
|
8
|
+
"LangGraphAgentAdapter",
|
|
9
|
+
),
|
|
10
|
+
"convert_message_content": (
|
|
11
|
+
"adapters.langgraph_adapter.formatter",
|
|
12
|
+
"convert_message_content",
|
|
13
|
+
),
|
|
14
|
+
"format_state_snapshot": (
|
|
15
|
+
"adapters.langgraph_adapter.formatter",
|
|
16
|
+
"format_state_snapshot",
|
|
17
|
+
),
|
|
18
|
+
"handle_stream_event": (
|
|
19
|
+
"adapters.langgraph_adapter.formatter",
|
|
20
|
+
"handle_stream_event",
|
|
21
|
+
),
|
|
22
|
+
"langchain_to_chat_message": (
|
|
23
|
+
"adapters.langgraph_adapter.formatter",
|
|
24
|
+
"langchain_to_chat_message",
|
|
25
|
+
),
|
|
26
|
+
"process_node_updates": (
|
|
27
|
+
"adapters.langgraph_adapter.formatter",
|
|
28
|
+
"process_node_updates",
|
|
29
|
+
),
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
__all__ = list(_EXPORTS)
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
def __getattr__(name: str):
|
|
36
|
+
if name not in _EXPORTS:
|
|
37
|
+
raise AttributeError(f"module 'adapters.langgraph_adapter' has no attribute '{name}'")
|
|
38
|
+
module_name, attr_name = _EXPORTS[name]
|
|
39
|
+
module = import_module(module_name)
|
|
40
|
+
value = getattr(module, attr_name)
|
|
41
|
+
globals()[name] = value
|
|
42
|
+
return value
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
if TYPE_CHECKING:
|
|
46
|
+
from adapters.langgraph_adapter.formatter import (
|
|
47
|
+
convert_message_content,
|
|
48
|
+
format_state_snapshot,
|
|
49
|
+
handle_stream_event,
|
|
50
|
+
langchain_to_chat_message,
|
|
51
|
+
process_node_updates,
|
|
52
|
+
)
|
|
53
|
+
from adapters.langgraph_adapter.langgraph_adapter import LangGraphAgentAdapter
|