fastworkflow 2.15.5__py3-none-any.whl → 2.17.13__py3-none-any.whl
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.
- fastworkflow/_workflows/command_metadata_extraction/_commands/ErrorCorrection/you_misunderstood.py +1 -1
- fastworkflow/_workflows/command_metadata_extraction/_commands/IntentDetection/what_can_i_do.py +16 -2
- fastworkflow/_workflows/command_metadata_extraction/_commands/wildcard.py +27 -570
- fastworkflow/_workflows/command_metadata_extraction/intent_detection.py +360 -0
- fastworkflow/_workflows/command_metadata_extraction/parameter_extraction.py +411 -0
- fastworkflow/chat_session.py +379 -206
- fastworkflow/cli.py +80 -165
- fastworkflow/command_context_model.py +73 -7
- fastworkflow/command_executor.py +14 -5
- fastworkflow/command_metadata_api.py +106 -6
- fastworkflow/examples/fastworkflow.env +2 -1
- fastworkflow/examples/fastworkflow.passwords.env +2 -1
- fastworkflow/examples/retail_workflow/_commands/exchange_delivered_order_items.py +32 -3
- fastworkflow/examples/retail_workflow/_commands/find_user_id_by_email.py +6 -5
- fastworkflow/examples/retail_workflow/_commands/modify_pending_order_items.py +32 -3
- fastworkflow/examples/retail_workflow/_commands/return_delivered_order_items.py +13 -2
- fastworkflow/examples/retail_workflow/_commands/transfer_to_human_agents.py +1 -1
- fastworkflow/intent_clarification_agent.py +131 -0
- fastworkflow/mcp_server.py +3 -3
- fastworkflow/run/__main__.py +33 -40
- fastworkflow/run_fastapi_mcp/README.md +373 -0
- fastworkflow/run_fastapi_mcp/__main__.py +1300 -0
- fastworkflow/run_fastapi_mcp/conversation_store.py +391 -0
- fastworkflow/run_fastapi_mcp/jwt_manager.py +341 -0
- fastworkflow/run_fastapi_mcp/mcp_specific.py +103 -0
- fastworkflow/run_fastapi_mcp/redoc_2_standalone_html.py +40 -0
- fastworkflow/run_fastapi_mcp/utils.py +517 -0
- fastworkflow/train/__main__.py +1 -1
- fastworkflow/utils/chat_adapter.py +99 -0
- fastworkflow/utils/python_utils.py +4 -4
- fastworkflow/utils/react.py +258 -0
- fastworkflow/utils/signatures.py +338 -139
- fastworkflow/workflow.py +1 -5
- fastworkflow/workflow_agent.py +185 -133
- {fastworkflow-2.15.5.dist-info → fastworkflow-2.17.13.dist-info}/METADATA +16 -18
- {fastworkflow-2.15.5.dist-info → fastworkflow-2.17.13.dist-info}/RECORD +40 -30
- fastworkflow/run_agent/__main__.py +0 -294
- fastworkflow/run_agent/agent_module.py +0 -194
- /fastworkflow/{run_agent → run_fastapi_mcp}/__init__.py +0 -0
- {fastworkflow-2.15.5.dist-info → fastworkflow-2.17.13.dist-info}/LICENSE +0 -0
- {fastworkflow-2.15.5.dist-info → fastworkflow-2.17.13.dist-info}/WHEEL +0 -0
- {fastworkflow-2.15.5.dist-info → fastworkflow-2.17.13.dist-info}/entry_points.txt +0 -0
|
@@ -0,0 +1,373 @@
|
|
|
1
|
+
# FastWorkflow FastAPI + MCP Service
|
|
2
|
+
|
|
3
|
+
HTTP + MCP interface for FastWorkflow workflows with synchronous and streaming execution.
|
|
4
|
+
|
|
5
|
+
## Overview
|
|
6
|
+
|
|
7
|
+
This service exposes FastWorkflow workflows as REST endpoints and as MCP tools, enabling clients to:
|
|
8
|
+
- Initialize workflow sessions per channel
|
|
9
|
+
- Submit natural language queries (agent mode)
|
|
10
|
+
- Execute deterministic commands (assistant mode)
|
|
11
|
+
- Perform explicit actions
|
|
12
|
+
- Manage conversations with persistent history
|
|
13
|
+
- Collect feedback on interactions
|
|
14
|
+
|
|
15
|
+
## Architecture
|
|
16
|
+
|
|
17
|
+
- **Session Management**: In-memory `ChannelSessionManager` with per-channel `ChatSession` instances
|
|
18
|
+
- **Persistence**: Rdict-backed conversation storage (one DB file per channel)
|
|
19
|
+
- **Execution**: Synchronous turn-based processing with queue-based communication
|
|
20
|
+
- **Tracing**: Traces are collected by default and included in synchronous responses or emitted incrementally during streaming
|
|
21
|
+
- **Streaming (REST)**: `/invoke_agent_stream` supports Streamable HTTP via NDJSON by default and SSE when requested in REST initialize
|
|
22
|
+
- **Streaming (MCP)**: MCP transport mounted at `/mcp`; tools stream partials via MCP transport by default or SSE when requested in MCP initialize
|
|
23
|
+
|
|
24
|
+
See [`docs/fastworkflow_fastapi_spec.md`](../../docs/fastworkflow_fastapi_spec.md) and [`docs/fastworkflow_fastapi_architecture.md`](../../docs/fastworkflow_fastapi_architecture.md) for complete specification and design.
|
|
25
|
+
|
|
26
|
+
## Running the Service
|
|
27
|
+
|
|
28
|
+
### Start Server (REST + MCP)
|
|
29
|
+
|
|
30
|
+
```bash
|
|
31
|
+
uvicorn services.run_fastapi.main:app --host 0.0.0.0 --port 8000
|
|
32
|
+
|
|
33
|
+
# MCP (auto-mounted via fastapi_mcp) will be available at `/mcp`.
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
### Access Documentation
|
|
37
|
+
|
|
38
|
+
Once running, visit:
|
|
39
|
+
- **Swagger UI**: http://localhost:8000/docs
|
|
40
|
+
- **ReDoc**: http://localhost:8000/redoc
|
|
41
|
+
- **Health Check**: http://localhost:8000/
|
|
42
|
+
|
|
43
|
+
## Environment Variables
|
|
44
|
+
|
|
45
|
+
Configure in your environment (loaded at process startup via CLI args or env load):
|
|
46
|
+
|
|
47
|
+
| Variable | Description | Required | Default |
|
|
48
|
+
|----------|-------------|----------|---------|
|
|
49
|
+
| `SPEEDDICT_FOLDERNAME` | Base folder for workflow contexts and conversation storage | Yes | - |
|
|
50
|
+
| `--expect_encrypted_jwt` | Enable full JWT signature verification (pass flag to require signed tokens) | No | False (no verification by default) |
|
|
51
|
+
|
|
52
|
+
Notes:
|
|
53
|
+
- Conversation DBs are stored under `SPEEDDICT_FOLDERNAME/channel_conversations` (directory is auto-created).
|
|
54
|
+
- `/conversations` now accepts a `limit` query parameter (default `20`).
|
|
55
|
+
- Shutdown waits up to 30 seconds for active turns (hard-coded).
|
|
56
|
+
|
|
57
|
+
## Auth Modes
|
|
58
|
+
|
|
59
|
+
### Trusted Mode (default): No Signature Verification
|
|
60
|
+
When `--expect_encrypted_jwt` is NOT set (trusted environments), the service still creates and returns JWT tokens from `/initialize`, but signature verification is disabled. Clients must include `Authorization: Bearer <access_token>` on subsequent requests.
|
|
61
|
+
|
|
62
|
+
```bash
|
|
63
|
+
uvicorn services.run_fastapi.main:app --workflow_path /path/to/workflow
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
Notes (trusted mode):
|
|
67
|
+
- `/initialize` returns access/refresh tokens and, if a startup command/action is provided, also returns the startup `CommandOutput`.
|
|
68
|
+
- Subsequent endpoints require `Authorization: Bearer <access_token>`.
|
|
69
|
+
- Traces include `user_id` when available (from JWT `uid` claim).
|
|
70
|
+
|
|
71
|
+
### Secure Mode: Signed JWTs with Verification
|
|
72
|
+
For production deployments requiring full RSA signature verification:
|
|
73
|
+
|
|
74
|
+
```bash
|
|
75
|
+
uvicorn services.run_fastapi.main:app --workflow_path /path/to/workflow --expect_encrypted_jwt
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
**Secure mode** (with `--expect_encrypted_jwt` flag):
|
|
79
|
+
- `/initialize` issues access/refresh tokens. Subsequent endpoints require `Authorization: Bearer <token>`.
|
|
80
|
+
- JWT claims include `sub` (channel_id) and `uid` (user_id when provided).
|
|
81
|
+
- Tokens are verified (signature, expiry, audience/issuer). Invalid or expired tokens are rejected.
|
|
82
|
+
- Recommended for production deployments in untrusted environments
|
|
83
|
+
|
|
84
|
+
### Token Access in Workflow Context
|
|
85
|
+
|
|
86
|
+
In secure mode, JWT tokens are passed to workflows via `workflow_context['http_bearer_token']` to support authenticated upstream calls. In trusted mode, tokens are not created/returned and `http_bearer_token` is absent.
|
|
87
|
+
|
|
88
|
+
**Important notes:**
|
|
89
|
+
- The token is **only available to authenticated endpoints** (those using `get_session_and_ensure_runtime` dependency)
|
|
90
|
+
- The token is stored in the workflow context dictionary under the key `http_bearer_token`
|
|
91
|
+
- Token is **automatically updated** on every authenticated request, ensuring workflows always have the current valid token
|
|
92
|
+
- Token expiration is **automatically verified** by `verify_token()` in both secure mode (`--expect_encrypted_jwt` flag) and trusted network mode
|
|
93
|
+
- In secure mode: Full cryptographic signature verification + expiration checking
|
|
94
|
+
- In trusted network mode: Expiration checking is performed (signature verification disabled)
|
|
95
|
+
- Tokens should be treated as sensitive data and handled securely in workflows
|
|
96
|
+
- The `/initialize` endpoint is unauthenticated and does NOT provide a token to the workflow context; tokens are only available after calling `/initialize` and using the returned token in subsequent requests
|
|
97
|
+
|
|
98
|
+
**Example usage in workflow:**
|
|
99
|
+
|
|
100
|
+
```python
|
|
101
|
+
# In workflow code
|
|
102
|
+
workflow_context = self._context
|
|
103
|
+
bearer_token = workflow_context.get('http_bearer_token')
|
|
104
|
+
|
|
105
|
+
# Use token for API calls
|
|
106
|
+
headers = {"Authorization": f"Bearer {bearer_token}"}
|
|
107
|
+
response = requests.get("https://api.example.com/data", headers=headers)
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
## API Endpoints (REST)
|
|
111
|
+
|
|
112
|
+
### `POST /initialize`
|
|
113
|
+
Initialize a session for a channel. Workflow configuration is loaded at server startup from CLI args/env.
|
|
114
|
+
|
|
115
|
+
**Request:**
|
|
116
|
+
```json
|
|
117
|
+
{
|
|
118
|
+
"channel_id": "channel-123",
|
|
119
|
+
"user_id": "user-9",
|
|
120
|
+
"stream_format": "ndjson",
|
|
121
|
+
"startup_command": "load_workflow ...",
|
|
122
|
+
"startup_action": {
|
|
123
|
+
"command_name": "find_orders",
|
|
124
|
+
"parameters": {"channel_id": 42}
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
```
|
|
128
|
+
|
|
129
|
+
**Rules:**
|
|
130
|
+
- `channel_id` is required.
|
|
131
|
+
- Exactly one of `startup_command` or `startup_action` may be provided (or neither).
|
|
132
|
+
- If startup is provided, `user_id` is required and recorded in the initial trace.
|
|
133
|
+
- `stream_format` controls REST streaming format for `/invoke_agent_stream` (NDJSON default, SSE optional).
|
|
134
|
+
|
|
135
|
+
**Response:**
|
|
136
|
+
```json
|
|
137
|
+
{
|
|
138
|
+
"access_token": "eyJhbGci...",
|
|
139
|
+
"refresh_token": "eyJhbGci...",
|
|
140
|
+
"token_type": "bearer",
|
|
141
|
+
"expires_in": 3600,
|
|
142
|
+
"startup_output": { /* CommandOutput, present only if startup was executed */ }
|
|
143
|
+
}
|
|
144
|
+
```
|
|
145
|
+
|
|
146
|
+
### `POST /invoke_agent`
|
|
147
|
+
Submit a natural language query for agentic processing.
|
|
148
|
+
|
|
149
|
+
**Headers:**
|
|
150
|
+
- `Authorization: Bearer <access_token>` (JWT contains `sub` for channel_id and optional `uid` for user_id)
|
|
151
|
+
|
|
152
|
+
**Request:**
|
|
153
|
+
```json
|
|
154
|
+
{
|
|
155
|
+
"user_query": "find orders for channel 42",
|
|
156
|
+
"timeout_seconds": 60
|
|
157
|
+
}
|
|
158
|
+
```
|
|
159
|
+
|
|
160
|
+
**Response:**
|
|
161
|
+
```json
|
|
162
|
+
{
|
|
163
|
+
"command_responses": [
|
|
164
|
+
{
|
|
165
|
+
"response": "Found 3 orders for channel 42",
|
|
166
|
+
"success": true,
|
|
167
|
+
"artifacts": {},
|
|
168
|
+
"next_actions": [],
|
|
169
|
+
"recommendations": []
|
|
170
|
+
}
|
|
171
|
+
],
|
|
172
|
+
"workflow_name": "default_workflow",
|
|
173
|
+
"context": "Order management context",
|
|
174
|
+
"command_name": "find_orders",
|
|
175
|
+
"command_parameters": "channel_id=42",
|
|
176
|
+
"traces": [...]
|
|
177
|
+
}
|
|
178
|
+
```
|
|
179
|
+
|
|
180
|
+
### `POST /invoke_assistant`
|
|
181
|
+
Submit a query for deterministic/assistant execution (no planning).
|
|
182
|
+
|
|
183
|
+
Same request/response format as `/invoke_agent`.
|
|
184
|
+
|
|
185
|
+
### `POST /perform_action`
|
|
186
|
+
Execute a specific workflow action directly (bypasses parameter extraction).
|
|
187
|
+
|
|
188
|
+
**Headers:**
|
|
189
|
+
- `Authorization: Bearer <access_token>` (JWT contains `sub` for channel_id and optional `uid` for user_id)
|
|
190
|
+
|
|
191
|
+
**Request:**
|
|
192
|
+
```json
|
|
193
|
+
{
|
|
194
|
+
"action": {
|
|
195
|
+
"command_name": "find_orders",
|
|
196
|
+
"parameters": {"channel_id": 42}
|
|
197
|
+
},
|
|
198
|
+
"timeout_seconds": 60
|
|
199
|
+
}
|
|
200
|
+
```
|
|
201
|
+
|
|
202
|
+
**Response:**
|
|
203
|
+
Same format as `/invoke_agent` (CommandOutput with traces).
|
|
204
|
+
|
|
205
|
+
### `POST /invoke_agent_stream`
|
|
206
|
+
Stream trace events and final `CommandOutput` via Streamable HTTP:
|
|
207
|
+
- NDJSON (default; `Content-Type: application/x-ndjson`)
|
|
208
|
+
- SSE (when REST `stream_format` is set to `sse`; `Content-Type: text/event-stream`)
|
|
209
|
+
|
|
210
|
+
**Headers:** Same as `/invoke_agent`.
|
|
211
|
+
|
|
212
|
+
## Conversation Management (REST)
|
|
213
|
+
|
|
214
|
+
### `POST /new_conversation`
|
|
215
|
+
Persist current conversation and start a new one.
|
|
216
|
+
|
|
217
|
+
**Request:**
|
|
218
|
+
```json
|
|
219
|
+
{}
|
|
220
|
+
```
|
|
221
|
+
|
|
222
|
+
**Response:**
|
|
223
|
+
```json
|
|
224
|
+
{
|
|
225
|
+
"status": "ok"
|
|
226
|
+
}
|
|
227
|
+
```
|
|
228
|
+
|
|
229
|
+
### `GET /conversations?channel_id={channel_id}&limit=20`
|
|
230
|
+
List conversations for a channel (most recent first). `limit` is optional; defaults to `20`.
|
|
231
|
+
|
|
232
|
+
**Response:**
|
|
233
|
+
```json
|
|
234
|
+
[
|
|
235
|
+
{
|
|
236
|
+
"conversation_id": 1,
|
|
237
|
+
"topic": "Order Management",
|
|
238
|
+
"summary": "...",
|
|
239
|
+
"created_at": 1234567890000,
|
|
240
|
+
"updated_at": 1234567890000
|
|
241
|
+
}
|
|
242
|
+
]
|
|
243
|
+
```
|
|
244
|
+
|
|
245
|
+
### `POST /activate_conversation`
|
|
246
|
+
Switch to a different conversation by ID.
|
|
247
|
+
|
|
248
|
+
**Request:**
|
|
249
|
+
```json
|
|
250
|
+
{
|
|
251
|
+
"conversation_id": 1
|
|
252
|
+
}
|
|
253
|
+
```
|
|
254
|
+
|
|
255
|
+
### `POST /post_feedback`
|
|
256
|
+
Attach feedback to the latest turn.
|
|
257
|
+
|
|
258
|
+
**Request:**
|
|
259
|
+
```json
|
|
260
|
+
{
|
|
261
|
+
"binary_or_numeric_score": true,
|
|
262
|
+
"nl_feedback": "Helpful response"
|
|
263
|
+
}
|
|
264
|
+
```
|
|
265
|
+
|
|
266
|
+
At least one of `binary_or_numeric_score` or `nl_feedback` must be provided.
|
|
267
|
+
|
|
268
|
+
## Admin Endpoints (REST-only; not exposed via MCP)
|
|
269
|
+
|
|
270
|
+
### `POST /admin/dump_all_conversations`
|
|
271
|
+
Export all conversations to JSONL.
|
|
272
|
+
|
|
273
|
+
**Request:**
|
|
274
|
+
```json
|
|
275
|
+
{
|
|
276
|
+
"output_folder": "/path/to/export"
|
|
277
|
+
}
|
|
278
|
+
```
|
|
279
|
+
|
|
280
|
+
**Response:**
|
|
281
|
+
```json
|
|
282
|
+
{
|
|
283
|
+
"file_path": "/path/to/export/all_conversations_1234567890.jsonl"
|
|
284
|
+
}
|
|
285
|
+
```
|
|
286
|
+
|
|
287
|
+
## Usage Examples
|
|
288
|
+
|
|
289
|
+
### REST initialize
|
|
290
|
+
```python
|
|
291
|
+
import requests
|
|
292
|
+
|
|
293
|
+
resp = requests.post("http://localhost:8000/initialize", json={
|
|
294
|
+
"channel_id": "alice",
|
|
295
|
+
"stream_format": "ndjson"
|
|
296
|
+
})
|
|
297
|
+
print(resp.json()) # {"access_token": "...", "refresh_token": "...", "token_type": "bearer", "expires_in": 3600}
|
|
298
|
+
```
|
|
299
|
+
|
|
300
|
+
### REST sync invoke
|
|
301
|
+
```python
|
|
302
|
+
# First get a token from /initialize
|
|
303
|
+
init_resp = requests.post("http://localhost:8000/initialize", json={
|
|
304
|
+
"channel_id": "alice",
|
|
305
|
+
"stream_format": "ndjson"
|
|
306
|
+
})
|
|
307
|
+
token_data = init_resp.json()
|
|
308
|
+
access_token = token_data["access_token"]
|
|
309
|
+
|
|
310
|
+
# Then use the token for authenticated requests
|
|
311
|
+
resp = requests.post("http://localhost:8000/invoke_agent",
|
|
312
|
+
headers={"Authorization": f"Bearer {access_token}"},
|
|
313
|
+
json={
|
|
314
|
+
"channel_query": "list all channels",
|
|
315
|
+
"timeout_seconds": 30
|
|
316
|
+
}
|
|
317
|
+
)
|
|
318
|
+
result = resp.json()
|
|
319
|
+
print(result["command_responses"])
|
|
320
|
+
print(result.get("traces"))
|
|
321
|
+
```
|
|
322
|
+
|
|
323
|
+
### MCP Quickstart
|
|
324
|
+
- Mount is available at `/mcp` (auto-exposed by fastapi_mcp).
|
|
325
|
+
- In secure mode, MCP clients use pre-configured long-lived access tokens (generated via `/admin/generate_mcp_token`).
|
|
326
|
+
- In trusted mode, clients must send `Authorization: Bearer <token>`; the JWT `sub` claim carries the `channel_id`.
|
|
327
|
+
- No need for initialize or refresh_token tools in MCP context.
|
|
328
|
+
|
|
329
|
+
MCP invoke agent (streaming):
|
|
330
|
+
|
|
331
|
+
```bash
|
|
332
|
+
# NDJSON (default for MCP)
|
|
333
|
+
curl -N -X POST http://localhost:8000/mcp/invoke_agent \
|
|
334
|
+
-H 'Content-Type: application/json' \
|
|
335
|
+
-H 'Accept: application/x-ndjson' \
|
|
336
|
+
-H 'Authorization: Bearer <your-mcp-token>' \
|
|
337
|
+
-d '{"channel_query":"find orders for channel 42","timeout_seconds":60}'
|
|
338
|
+
|
|
339
|
+
# SSE (if stream_format was set to "sse" during MCP setup)
|
|
340
|
+
curl -N -X POST http://localhost:8000/mcp/invoke_agent \
|
|
341
|
+
-H 'Content-Type: application/json' \
|
|
342
|
+
-H 'Accept: text/event-stream' \
|
|
343
|
+
-H 'Authorization: Bearer <your-mcp-token>' \
|
|
344
|
+
-d '{"channel_query":"find orders for channel 42","timeout_seconds":60}'
|
|
345
|
+
```
|
|
346
|
+
|
|
347
|
+
### MCP Prompts
|
|
348
|
+
The MCP mount registers two prompts for client discovery:
|
|
349
|
+
- `format-command(intent, metadata)`
|
|
350
|
+
- `clarify-params(error_message, metadata)`
|
|
351
|
+
|
|
352
|
+
## Testing
|
|
353
|
+
|
|
354
|
+
See [`tests/`](../../tests/) for unit and integration tests.
|
|
355
|
+
|
|
356
|
+
**Key test scenarios:**
|
|
357
|
+
- SessionManager concurrency and lifecycle
|
|
358
|
+
- Env loading from files only
|
|
359
|
+
- CLI argument validation (startup_command vs startup_action)
|
|
360
|
+
- Timeout behavior (504 on no output)
|
|
361
|
+
- Conversation persistence and listing
|
|
362
|
+
- Feedback attachment
|
|
363
|
+
- Initialize with startup command/action returns `startup_output` and records conversation
|
|
364
|
+
- Both trusted mode return tokens; secure mode tokens are encrypted
|
|
365
|
+
- Traces include `user_id` and `raw_command`
|
|
366
|
+
|
|
367
|
+
## Future Enhancements
|
|
368
|
+
|
|
369
|
+
- Session TTL and eviction policy
|
|
370
|
+
- Richer observability and structured logging
|
|
371
|
+
- Security: workflow path allow-list, authn/z
|
|
372
|
+
- Conversation history restoration on session resume
|
|
373
|
+
|