jaf-py 2.5.10__py3-none-any.whl → 2.5.12__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.
- jaf/__init__.py +154 -57
- jaf/a2a/__init__.py +42 -21
- jaf/a2a/agent.py +79 -126
- jaf/a2a/agent_card.py +87 -78
- jaf/a2a/client.py +30 -66
- jaf/a2a/examples/client_example.py +12 -12
- jaf/a2a/examples/integration_example.py +38 -47
- jaf/a2a/examples/server_example.py +56 -53
- jaf/a2a/memory/__init__.py +0 -4
- jaf/a2a/memory/cleanup.py +28 -21
- jaf/a2a/memory/factory.py +155 -133
- jaf/a2a/memory/providers/composite.py +21 -26
- jaf/a2a/memory/providers/in_memory.py +89 -83
- jaf/a2a/memory/providers/postgres.py +117 -115
- jaf/a2a/memory/providers/redis.py +128 -121
- jaf/a2a/memory/serialization.py +77 -87
- jaf/a2a/memory/tests/run_comprehensive_tests.py +112 -83
- jaf/a2a/memory/tests/test_cleanup.py +211 -94
- jaf/a2a/memory/tests/test_serialization.py +73 -68
- jaf/a2a/memory/tests/test_stress_concurrency.py +186 -133
- jaf/a2a/memory/tests/test_task_lifecycle.py +138 -120
- jaf/a2a/memory/types.py +91 -53
- jaf/a2a/protocol.py +95 -125
- jaf/a2a/server.py +90 -118
- jaf/a2a/standalone_client.py +30 -43
- jaf/a2a/tests/__init__.py +16 -33
- jaf/a2a/tests/run_tests.py +17 -53
- jaf/a2a/tests/test_agent.py +40 -140
- jaf/a2a/tests/test_client.py +54 -117
- jaf/a2a/tests/test_integration.py +28 -82
- jaf/a2a/tests/test_protocol.py +54 -139
- jaf/a2a/tests/test_types.py +50 -136
- jaf/a2a/types.py +58 -34
- jaf/cli.py +21 -41
- jaf/core/__init__.py +7 -1
- jaf/core/agent_tool.py +93 -72
- jaf/core/analytics.py +257 -207
- jaf/core/checkpoint.py +223 -0
- jaf/core/composition.py +249 -235
- jaf/core/engine.py +817 -519
- jaf/core/errors.py +55 -42
- jaf/core/guardrails.py +276 -202
- jaf/core/handoff.py +47 -31
- jaf/core/parallel_agents.py +69 -75
- jaf/core/performance.py +75 -73
- jaf/core/proxy.py +43 -44
- jaf/core/proxy_helpers.py +24 -27
- jaf/core/regeneration.py +220 -129
- jaf/core/state.py +68 -66
- jaf/core/streaming.py +115 -108
- jaf/core/tool_results.py +111 -101
- jaf/core/tools.py +114 -116
- jaf/core/tracing.py +310 -210
- jaf/core/types.py +403 -151
- jaf/core/workflows.py +209 -168
- jaf/exceptions.py +46 -38
- jaf/memory/__init__.py +1 -6
- jaf/memory/approval_storage.py +54 -77
- jaf/memory/factory.py +4 -4
- jaf/memory/providers/in_memory.py +216 -180
- jaf/memory/providers/postgres.py +216 -146
- jaf/memory/providers/redis.py +173 -116
- jaf/memory/types.py +70 -51
- jaf/memory/utils.py +36 -34
- jaf/plugins/__init__.py +12 -12
- jaf/plugins/base.py +105 -96
- jaf/policies/__init__.py +0 -1
- jaf/policies/handoff.py +37 -46
- jaf/policies/validation.py +76 -52
- jaf/providers/__init__.py +6 -3
- jaf/providers/mcp.py +97 -51
- jaf/providers/model.py +475 -283
- jaf/server/__init__.py +1 -1
- jaf/server/main.py +7 -11
- jaf/server/server.py +514 -359
- jaf/server/types.py +208 -52
- jaf/utils/__init__.py +17 -18
- jaf/utils/attachments.py +111 -116
- jaf/utils/document_processor.py +175 -174
- jaf/visualization/__init__.py +1 -1
- jaf/visualization/example.py +111 -110
- jaf/visualization/functional_core.py +46 -71
- jaf/visualization/graphviz.py +154 -189
- jaf/visualization/imperative_shell.py +7 -16
- jaf/visualization/types.py +8 -4
- {jaf_py-2.5.10.dist-info → jaf_py-2.5.12.dist-info}/METADATA +2 -2
- jaf_py-2.5.12.dist-info/RECORD +97 -0
- jaf_py-2.5.10.dist-info/RECORD +0 -96
- {jaf_py-2.5.10.dist-info → jaf_py-2.5.12.dist-info}/WHEEL +0 -0
- {jaf_py-2.5.10.dist-info → jaf_py-2.5.12.dist-info}/entry_points.txt +0 -0
- {jaf_py-2.5.10.dist-info → jaf_py-2.5.12.dist-info}/licenses/LICENSE +0 -0
- {jaf_py-2.5.10.dist-info → jaf_py-2.5.12.dist-info}/top_level.txt +0 -0
jaf/server/types.py
CHANGED
|
@@ -11,30 +11,40 @@ import base64
|
|
|
11
11
|
|
|
12
12
|
from pydantic import BaseModel, Field, field_validator, model_validator
|
|
13
13
|
|
|
14
|
-
from ..core.types import
|
|
14
|
+
from ..core.types import (
|
|
15
|
+
Agent,
|
|
16
|
+
RunConfig,
|
|
17
|
+
Attachment,
|
|
18
|
+
MessageContentPart,
|
|
19
|
+
get_text_content,
|
|
20
|
+
MessageId,
|
|
21
|
+
RegenerationRequest,
|
|
22
|
+
)
|
|
15
23
|
from ..memory.types import MemoryProvider
|
|
16
24
|
|
|
17
|
-
Ctx = TypeVar(
|
|
25
|
+
Ctx = TypeVar("Ctx")
|
|
26
|
+
|
|
18
27
|
|
|
19
28
|
# Pydantic models for attachments to work with HTTP API
|
|
20
29
|
class HttpAttachment(BaseModel):
|
|
21
30
|
"""HTTP attachment format for API requests."""
|
|
22
|
-
|
|
31
|
+
|
|
32
|
+
kind: Literal["image", "document", "file"]
|
|
23
33
|
mime_type: Optional[str] = None
|
|
24
34
|
name: Optional[str] = None
|
|
25
35
|
url: Optional[str] = None
|
|
26
36
|
data: Optional[str] = None # Base64 encoded data
|
|
27
37
|
format: Optional[str] = None
|
|
28
38
|
use_litellm_format: Optional[bool] = None
|
|
29
|
-
|
|
30
|
-
@model_validator(mode=
|
|
31
|
-
def validate_url_or_data_present(self) ->
|
|
39
|
+
|
|
40
|
+
@model_validator(mode="after")
|
|
41
|
+
def validate_url_or_data_present(self) -> "HttpAttachment":
|
|
32
42
|
"""Validate that at least one of url or data is present."""
|
|
33
43
|
if self.url is None and self.data is None:
|
|
34
44
|
raise ValueError("At least one of 'url' or 'data' must be provided")
|
|
35
45
|
return self
|
|
36
|
-
|
|
37
|
-
@field_validator(
|
|
46
|
+
|
|
47
|
+
@field_validator("data")
|
|
38
48
|
@classmethod
|
|
39
49
|
def validate_base64_data(cls, v: Optional[str]) -> Optional[str]:
|
|
40
50
|
"""Validate that data is proper base64 encoded."""
|
|
@@ -48,18 +58,20 @@ class HttpAttachment(BaseModel):
|
|
|
48
58
|
except Exception as e:
|
|
49
59
|
raise ValueError(f"Invalid base64 encoding: {str(e)}")
|
|
50
60
|
return v
|
|
51
|
-
|
|
52
|
-
@model_validator(mode=
|
|
53
|
-
def validate_mime_type_consistency(self) ->
|
|
61
|
+
|
|
62
|
+
@model_validator(mode="after")
|
|
63
|
+
def validate_mime_type_consistency(self) -> "HttpAttachment":
|
|
54
64
|
"""Validate that mime_type is consistent with kind."""
|
|
55
65
|
if self.mime_type is not None and self.kind is not None:
|
|
56
|
-
if self.kind ==
|
|
57
|
-
raise ValueError(
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
self.mime_type.startswith(
|
|
66
|
+
if self.kind == "image" and not self.mime_type.startswith("image/"):
|
|
67
|
+
raise ValueError(
|
|
68
|
+
f"For kind='image', mime_type must start with 'image/'. Got: {self.mime_type}"
|
|
69
|
+
)
|
|
70
|
+
|
|
71
|
+
elif self.kind == "document" and not (
|
|
72
|
+
self.mime_type.startswith("application/")
|
|
73
|
+
or self.mime_type.startswith("text/")
|
|
74
|
+
or self.mime_type.startswith("document/")
|
|
63
75
|
):
|
|
64
76
|
raise ValueError(
|
|
65
77
|
f"For kind='document', mime_type must start with 'application/', 'text/', "
|
|
@@ -67,99 +79,137 @@ class HttpAttachment(BaseModel):
|
|
|
67
79
|
)
|
|
68
80
|
return self
|
|
69
81
|
|
|
82
|
+
|
|
70
83
|
class HttpMessageContentPart(BaseModel):
|
|
71
84
|
"""HTTP message content part for multi-part messages."""
|
|
72
|
-
|
|
85
|
+
|
|
86
|
+
type: Literal["text", "image_url", "file"]
|
|
73
87
|
text: Optional[str] = None
|
|
74
88
|
image_url: Optional[Dict[str, Any]] = None
|
|
75
89
|
file: Optional[Dict[str, Any]] = None
|
|
76
|
-
|
|
77
|
-
@model_validator(mode=
|
|
78
|
-
def validate_content_consistency(self) ->
|
|
90
|
+
|
|
91
|
+
@model_validator(mode="after")
|
|
92
|
+
def validate_content_consistency(self) -> "HttpMessageContentPart":
|
|
79
93
|
"""Validate that exactly one field is populated and it matches the declared type."""
|
|
80
94
|
# Count non-None content fields
|
|
81
95
|
populated_fields = []
|
|
82
96
|
if self.text is not None:
|
|
83
|
-
populated_fields.append(
|
|
97
|
+
populated_fields.append("text")
|
|
84
98
|
if self.image_url is not None:
|
|
85
|
-
populated_fields.append(
|
|
99
|
+
populated_fields.append("image_url")
|
|
86
100
|
if self.file is not None:
|
|
87
|
-
populated_fields.append(
|
|
88
|
-
|
|
101
|
+
populated_fields.append("file")
|
|
102
|
+
|
|
89
103
|
# Check if exactly one field is populated
|
|
90
104
|
if len(populated_fields) != 1:
|
|
91
|
-
raise ValueError(
|
|
92
|
-
|
|
105
|
+
raise ValueError(
|
|
106
|
+
f"Exactly one content field must be populated. Found {len(populated_fields)}: {populated_fields}"
|
|
107
|
+
)
|
|
108
|
+
|
|
93
109
|
# Check that the populated field matches the declared type
|
|
94
110
|
populated_field = populated_fields[0]
|
|
95
|
-
if self.type ==
|
|
96
|
-
raise ValueError(
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
elif self.type ==
|
|
100
|
-
raise ValueError(
|
|
101
|
-
|
|
111
|
+
if self.type == "text" and populated_field != "text":
|
|
112
|
+
raise ValueError(
|
|
113
|
+
f"For type='text', the 'text' field must be populated, but found '{populated_field}' instead"
|
|
114
|
+
)
|
|
115
|
+
elif self.type == "image_url" and populated_field != "image_url":
|
|
116
|
+
raise ValueError(
|
|
117
|
+
f"For type='image_url', the 'image_url' field must be populated, but found '{populated_field}' instead"
|
|
118
|
+
)
|
|
119
|
+
elif self.type == "file" and populated_field != "file":
|
|
120
|
+
raise ValueError(
|
|
121
|
+
f"For type='file', the 'file' field must be populated, but found '{populated_field}' instead"
|
|
122
|
+
)
|
|
123
|
+
|
|
102
124
|
return self
|
|
103
125
|
|
|
126
|
+
|
|
104
127
|
# HTTP Message types
|
|
105
128
|
class HttpMessage(BaseModel):
|
|
106
129
|
"""HTTP message format for API requests."""
|
|
107
|
-
|
|
130
|
+
|
|
131
|
+
role: Literal["user", "assistant", "system", "tool"]
|
|
108
132
|
content: Union[str, List[HttpMessageContentPart]]
|
|
109
133
|
attachments: Optional[List[HttpAttachment]] = None
|
|
110
134
|
tool_call_id: Optional[str] = None
|
|
111
135
|
tool_calls: Optional[List[Dict[str, Any]]] = None
|
|
112
136
|
|
|
137
|
+
|
|
113
138
|
# Approval types for HITL
|
|
114
139
|
class ApprovalMessage(BaseModel):
|
|
115
140
|
"""Approval message for tool execution."""
|
|
116
|
-
|
|
141
|
+
|
|
142
|
+
type: Literal["approval"] = "approval"
|
|
117
143
|
session_id: str = Field(..., description="Session ID for the approval")
|
|
118
144
|
tool_call_id: str = Field(..., description="ID of the tool call being approved")
|
|
119
145
|
approved: bool = Field(..., description="Whether the tool execution is approved")
|
|
120
|
-
additional_context: Optional[Dict[str, Any]] = Field(
|
|
146
|
+
additional_context: Optional[Dict[str, Any]] = Field(
|
|
147
|
+
default=None, description="Additional context for the approval"
|
|
148
|
+
)
|
|
149
|
+
|
|
121
150
|
|
|
122
151
|
# Request types
|
|
123
152
|
class ChatRequest(BaseModel):
|
|
124
153
|
"""Request format for chat endpoints."""
|
|
154
|
+
|
|
125
155
|
messages: List[HttpMessage]
|
|
126
156
|
agent_name: str = Field(..., description="Name of the agent to use")
|
|
127
|
-
context: Optional[Dict[str, Any]] = Field(
|
|
157
|
+
context: Optional[Dict[str, Any]] = Field(
|
|
158
|
+
default_factory=dict, description="Context data for the agent"
|
|
159
|
+
)
|
|
128
160
|
max_turns: Optional[int] = Field(default=10, description="Maximum number of turns")
|
|
129
161
|
stream: bool = Field(default=False, description="Whether to stream the response")
|
|
130
|
-
conversation_id: Optional[str] = Field(
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
162
|
+
conversation_id: Optional[str] = Field(
|
|
163
|
+
default=None, description="Conversation ID for memory persistence"
|
|
164
|
+
)
|
|
165
|
+
memory: Optional[Dict[str, Any]] = Field(
|
|
166
|
+
default=None, description="Memory configuration override"
|
|
167
|
+
)
|
|
168
|
+
store_on_completion: Optional[bool] = Field(
|
|
169
|
+
default=None, description="Whether to store conversation on completion"
|
|
170
|
+
)
|
|
171
|
+
approvals: Optional[List[ApprovalMessage]] = Field(
|
|
172
|
+
default=None, description="Approval decisions for tool calls"
|
|
173
|
+
)
|
|
174
|
+
|
|
134
175
|
|
|
135
176
|
# Interruption types for HITL
|
|
136
177
|
class ToolCallInterruption(BaseModel):
|
|
137
178
|
"""Tool call interruption data."""
|
|
179
|
+
|
|
138
180
|
id: str
|
|
139
|
-
type: Literal[
|
|
181
|
+
type: Literal["function"] = "function"
|
|
140
182
|
function: Dict[str, str] # name and arguments
|
|
141
183
|
|
|
184
|
+
|
|
142
185
|
class InterruptionData(BaseModel):
|
|
143
186
|
"""Interruption information."""
|
|
144
|
-
|
|
187
|
+
|
|
188
|
+
type: Literal["tool_approval"] = "tool_approval"
|
|
145
189
|
tool_call: Optional[ToolCallInterruption]
|
|
146
190
|
session_id: str
|
|
147
191
|
|
|
192
|
+
|
|
148
193
|
# Base outcome types
|
|
149
194
|
class BaseOutcomeData(BaseModel):
|
|
150
195
|
"""Base outcome data."""
|
|
151
|
-
|
|
196
|
+
|
|
197
|
+
status: Literal["completed", "error", "max_turns", "interrupted"]
|
|
152
198
|
output: Optional[str] = None
|
|
153
199
|
error: Optional[Any] = None
|
|
154
200
|
|
|
201
|
+
|
|
155
202
|
class InterruptedOutcomeData(BaseOutcomeData):
|
|
156
203
|
"""Outcome data for interrupted runs."""
|
|
157
|
-
|
|
204
|
+
|
|
205
|
+
status: Literal["interrupted"] = "interrupted"
|
|
158
206
|
interruptions: Optional[List[InterruptionData]] = None
|
|
159
207
|
|
|
208
|
+
|
|
160
209
|
# Response types
|
|
161
210
|
class CompletedChatData(BaseModel):
|
|
162
211
|
"""Data for successful chat completion."""
|
|
212
|
+
|
|
163
213
|
run_id: str
|
|
164
214
|
trace_id: str
|
|
165
215
|
messages: List[HttpMessage]
|
|
@@ -168,115 +218,153 @@ class CompletedChatData(BaseModel):
|
|
|
168
218
|
execution_time_ms: int
|
|
169
219
|
conversation_id: Optional[str] = None
|
|
170
220
|
|
|
221
|
+
|
|
171
222
|
class ChatResponse(BaseModel):
|
|
172
223
|
"""Response format for chat endpoints."""
|
|
224
|
+
|
|
173
225
|
success: bool
|
|
174
226
|
data: Optional[CompletedChatData] = None
|
|
175
227
|
error: Optional[str] = None
|
|
176
228
|
|
|
229
|
+
|
|
177
230
|
class AgentInfo(BaseModel):
|
|
178
231
|
"""Information about an available agent."""
|
|
232
|
+
|
|
179
233
|
name: str
|
|
180
234
|
description: str
|
|
181
235
|
tools: List[str]
|
|
182
236
|
|
|
237
|
+
|
|
183
238
|
class AgentListData(BaseModel):
|
|
184
239
|
"""Data for agent list response."""
|
|
240
|
+
|
|
185
241
|
agents: List[AgentInfo]
|
|
186
242
|
|
|
243
|
+
|
|
187
244
|
class AgentListResponse(BaseModel):
|
|
188
245
|
"""Response format for agent list endpoint."""
|
|
246
|
+
|
|
189
247
|
success: bool
|
|
190
248
|
data: Optional[AgentListData] = None
|
|
191
249
|
error: Optional[str] = None
|
|
192
250
|
|
|
251
|
+
|
|
193
252
|
class HealthResponse(BaseModel):
|
|
194
253
|
"""Response format for health check endpoint."""
|
|
195
|
-
|
|
254
|
+
|
|
255
|
+
status: Literal["healthy", "unhealthy"]
|
|
196
256
|
timestamp: str
|
|
197
257
|
version: str
|
|
198
258
|
uptime: int # milliseconds
|
|
199
259
|
|
|
260
|
+
|
|
200
261
|
# Memory-specific response types
|
|
201
262
|
class ConversationData(BaseModel):
|
|
202
263
|
"""Data for a conversation."""
|
|
264
|
+
|
|
203
265
|
conversation_id: str
|
|
204
266
|
user_id: Optional[str] = None
|
|
205
267
|
messages: List[Dict[str, Any]]
|
|
206
268
|
metadata: Optional[Dict[str, Any]] = None
|
|
207
269
|
|
|
270
|
+
|
|
208
271
|
class ConversationResponse(BaseModel):
|
|
209
272
|
"""Response format for conversation endpoints."""
|
|
273
|
+
|
|
210
274
|
success: bool
|
|
211
275
|
data: Optional[ConversationData] = None
|
|
212
276
|
error: Optional[str] = None
|
|
213
277
|
|
|
278
|
+
|
|
214
279
|
class MemoryHealthData(BaseModel):
|
|
215
280
|
"""Data for memory health check."""
|
|
281
|
+
|
|
216
282
|
healthy: bool
|
|
217
283
|
provider: str
|
|
218
284
|
latency_ms: float
|
|
219
285
|
details: Optional[Dict[str, Any]] = None
|
|
220
286
|
|
|
287
|
+
|
|
221
288
|
class MemoryHealthResponse(BaseModel):
|
|
222
289
|
"""Response format for memory health endpoint."""
|
|
290
|
+
|
|
223
291
|
success: bool
|
|
224
292
|
data: Optional[MemoryHealthData] = None
|
|
225
293
|
error: Optional[str] = None
|
|
226
294
|
|
|
295
|
+
|
|
227
296
|
class DeleteConversationData(BaseModel):
|
|
228
297
|
"""Data for delete conversation response."""
|
|
298
|
+
|
|
229
299
|
conversation_id: str
|
|
230
300
|
deleted: bool
|
|
231
301
|
|
|
302
|
+
|
|
232
303
|
class DeleteConversationResponse(BaseModel):
|
|
233
304
|
"""Response format for delete conversation endpoint."""
|
|
305
|
+
|
|
234
306
|
success: bool
|
|
235
307
|
data: Optional[DeleteConversationData] = None
|
|
236
308
|
error: Optional[str] = None
|
|
237
309
|
|
|
310
|
+
|
|
238
311
|
# Server configuration
|
|
239
312
|
@dataclass
|
|
240
313
|
class ServerConfig(Generic[Ctx]):
|
|
241
314
|
"""Configuration for the JAF HTTP server."""
|
|
315
|
+
|
|
242
316
|
agent_registry: Dict[str, Agent[Ctx, Any]]
|
|
243
317
|
run_config: RunConfig[Ctx]
|
|
244
|
-
host: str =
|
|
318
|
+
host: str = "127.0.0.1"
|
|
245
319
|
port: int = 3000
|
|
246
320
|
cors: Union[bool, Dict[str, Any]] = True
|
|
247
321
|
default_memory_provider: Optional[MemoryProvider] = None
|
|
248
322
|
|
|
323
|
+
|
|
249
324
|
# Approval response types
|
|
250
325
|
class PendingApprovalData(BaseModel):
|
|
251
326
|
"""Data for a pending approval."""
|
|
327
|
+
|
|
252
328
|
conversation_id: str
|
|
253
329
|
tool_call_id: str
|
|
254
330
|
tool_name: str
|
|
255
331
|
args: Dict[str, Any]
|
|
256
332
|
signature: Optional[str] = None
|
|
257
|
-
status: Literal[
|
|
333
|
+
status: Literal["pending"] = "pending"
|
|
258
334
|
session_id: Optional[str] = None
|
|
259
335
|
|
|
336
|
+
|
|
260
337
|
class PendingApprovalsData(BaseModel):
|
|
261
338
|
"""Data for pending approvals response."""
|
|
339
|
+
|
|
262
340
|
pending: List[PendingApprovalData]
|
|
263
341
|
|
|
342
|
+
|
|
264
343
|
class PendingApprovalsResponse(BaseModel):
|
|
265
344
|
"""Response format for pending approvals endpoint."""
|
|
345
|
+
|
|
266
346
|
success: bool
|
|
267
347
|
data: Optional[PendingApprovalsData] = None
|
|
268
348
|
error: Optional[str] = None
|
|
269
349
|
|
|
350
|
+
|
|
270
351
|
# Regeneration types
|
|
271
352
|
class RegenerationHttpRequest(BaseModel):
|
|
272
353
|
"""HTTP request format for conversation regeneration."""
|
|
354
|
+
|
|
273
355
|
message_id: str = Field(..., description="ID of the message to regenerate from")
|
|
274
356
|
agent_name: str = Field(..., description="Name of the agent to use for regeneration")
|
|
275
|
-
context: Optional[Dict[str, Any]] = Field(
|
|
276
|
-
|
|
357
|
+
context: Optional[Dict[str, Any]] = Field(
|
|
358
|
+
default=None, description="Optional context override for regeneration"
|
|
359
|
+
)
|
|
360
|
+
max_turns: Optional[int] = Field(
|
|
361
|
+
default=10, description="Maximum number of turns for regeneration"
|
|
362
|
+
)
|
|
363
|
+
|
|
277
364
|
|
|
278
365
|
class RegenerationData(BaseModel):
|
|
279
366
|
"""Data for successful regeneration response."""
|
|
367
|
+
|
|
280
368
|
regeneration_id: str
|
|
281
369
|
conversation_id: str
|
|
282
370
|
original_message_count: int
|
|
@@ -287,36 +375,104 @@ class RegenerationData(BaseModel):
|
|
|
287
375
|
turn_count: int
|
|
288
376
|
execution_time_ms: int
|
|
289
377
|
|
|
378
|
+
|
|
290
379
|
class RegenerationResponse(BaseModel):
|
|
291
380
|
"""Response format for regeneration endpoints."""
|
|
381
|
+
|
|
292
382
|
success: bool
|
|
293
383
|
data: Optional[RegenerationData] = None
|
|
294
384
|
error: Optional[str] = None
|
|
295
385
|
|
|
386
|
+
|
|
296
387
|
class RegenerationPointData(BaseModel):
|
|
297
388
|
"""Data for a regeneration point."""
|
|
389
|
+
|
|
298
390
|
regeneration_id: str
|
|
299
391
|
message_id: str
|
|
300
392
|
timestamp: int
|
|
301
393
|
original_message_count: int
|
|
302
394
|
truncated_at_index: int
|
|
303
395
|
|
|
396
|
+
|
|
304
397
|
class RegenerationHistoryData(BaseModel):
|
|
305
398
|
"""Data for regeneration history response."""
|
|
399
|
+
|
|
306
400
|
conversation_id: str
|
|
307
401
|
regeneration_points: List[RegenerationPointData]
|
|
308
402
|
|
|
403
|
+
|
|
309
404
|
class RegenerationHistoryResponse(BaseModel):
|
|
310
405
|
"""Response format for regeneration history endpoint."""
|
|
406
|
+
|
|
311
407
|
success: bool
|
|
312
408
|
data: Optional[RegenerationHistoryData] = None
|
|
313
409
|
error: Optional[str] = None
|
|
314
410
|
|
|
411
|
+
|
|
412
|
+
# Checkpoint types
|
|
413
|
+
class CheckpointHttpRequest(BaseModel):
|
|
414
|
+
"""HTTP request format for conversation checkpoint."""
|
|
415
|
+
|
|
416
|
+
message_id: str = Field(
|
|
417
|
+
..., description="ID of the message to checkpoint after (this message is kept)"
|
|
418
|
+
)
|
|
419
|
+
context: Optional[Dict[str, Any]] = Field(
|
|
420
|
+
default=None, description="Optional context for the checkpoint"
|
|
421
|
+
)
|
|
422
|
+
|
|
423
|
+
|
|
424
|
+
class CheckpointData(BaseModel):
|
|
425
|
+
"""Data for successful checkpoint response."""
|
|
426
|
+
|
|
427
|
+
checkpoint_id: str
|
|
428
|
+
conversation_id: str
|
|
429
|
+
original_message_count: int
|
|
430
|
+
checkpointed_at_index: int
|
|
431
|
+
checkpointed_message_id: str
|
|
432
|
+
messages: List[HttpMessage]
|
|
433
|
+
execution_time_ms: int
|
|
434
|
+
|
|
435
|
+
|
|
436
|
+
class CheckpointResponse(BaseModel):
|
|
437
|
+
"""Response format for checkpoint endpoints."""
|
|
438
|
+
|
|
439
|
+
success: bool
|
|
440
|
+
data: Optional[CheckpointData] = None
|
|
441
|
+
error: Optional[str] = None
|
|
442
|
+
|
|
443
|
+
|
|
444
|
+
class CheckpointPointData(BaseModel):
|
|
445
|
+
"""Data for a checkpoint point."""
|
|
446
|
+
|
|
447
|
+
checkpoint_id: str
|
|
448
|
+
checkpoint_point: str
|
|
449
|
+
timestamp: int
|
|
450
|
+
original_message_count: int
|
|
451
|
+
checkpointed_at_index: int
|
|
452
|
+
checkpointed_messages: int
|
|
453
|
+
|
|
454
|
+
|
|
455
|
+
class CheckpointHistoryData(BaseModel):
|
|
456
|
+
"""Data for checkpoint history response."""
|
|
457
|
+
|
|
458
|
+
conversation_id: str
|
|
459
|
+
checkpoint_points: List[CheckpointPointData]
|
|
460
|
+
|
|
461
|
+
|
|
462
|
+
class CheckpointHistoryResponse(BaseModel):
|
|
463
|
+
"""Response format for checkpoint history endpoint."""
|
|
464
|
+
|
|
465
|
+
success: bool
|
|
466
|
+
data: Optional[CheckpointHistoryData] = None
|
|
467
|
+
error: Optional[str] = None
|
|
468
|
+
|
|
469
|
+
|
|
315
470
|
# Validation schemas
|
|
316
471
|
def validate_chat_request(data: Dict[str, Any]) -> ChatRequest:
|
|
317
472
|
"""Validate and parse a chat request."""
|
|
318
473
|
return ChatRequest.model_validate(data)
|
|
319
474
|
|
|
475
|
+
|
|
320
476
|
def validate_regeneration_request(data: Dict[str, Any]) -> RegenerationHttpRequest:
|
|
321
477
|
"""Validate and parse a regeneration request."""
|
|
322
478
|
return RegenerationHttpRequest.model_validate(data)
|
jaf/utils/__init__.py
CHANGED
|
@@ -16,7 +16,7 @@ from .attachments import (
|
|
|
16
16
|
ATTACHMENT_LIMITS,
|
|
17
17
|
)
|
|
18
18
|
|
|
19
|
-
# Import document processing utilities
|
|
19
|
+
# Import document processing utilities
|
|
20
20
|
from .document_processor import (
|
|
21
21
|
extract_document_content,
|
|
22
22
|
is_document_supported,
|
|
@@ -30,21 +30,20 @@ from .document_processor import (
|
|
|
30
30
|
|
|
31
31
|
__all__ = [
|
|
32
32
|
# Attachment utilities
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
33
|
+
"make_image_attachment",
|
|
34
|
+
"make_file_attachment",
|
|
35
|
+
"make_document_attachment",
|
|
36
|
+
"validate_attachment",
|
|
37
|
+
"assert_non_empty_attachment",
|
|
38
|
+
"AttachmentValidationError",
|
|
39
|
+
"ATTACHMENT_LIMITS",
|
|
41
40
|
# Document processing
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
]
|
|
41
|
+
"extract_document_content",
|
|
42
|
+
"is_document_supported",
|
|
43
|
+
"get_document_description",
|
|
44
|
+
"get_missing_dependencies",
|
|
45
|
+
"check_dependencies",
|
|
46
|
+
"ProcessedDocument",
|
|
47
|
+
"DocumentProcessingError",
|
|
48
|
+
"NetworkError",
|
|
49
|
+
]
|