rakam-systems-core 0.1.1rc7__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.
- rakam_systems_core/__init__.py +41 -0
- rakam_systems_core/ai_core/__init__.py +68 -0
- rakam_systems_core/ai_core/base.py +142 -0
- rakam_systems_core/ai_core/config.py +12 -0
- rakam_systems_core/ai_core/config_loader.py +580 -0
- rakam_systems_core/ai_core/config_schema.py +395 -0
- rakam_systems_core/ai_core/interfaces/__init__.py +30 -0
- rakam_systems_core/ai_core/interfaces/agent.py +83 -0
- rakam_systems_core/ai_core/interfaces/chat_history.py +122 -0
- rakam_systems_core/ai_core/interfaces/chunker.py +11 -0
- rakam_systems_core/ai_core/interfaces/embedding_model.py +10 -0
- rakam_systems_core/ai_core/interfaces/indexer.py +10 -0
- rakam_systems_core/ai_core/interfaces/llm_gateway.py +139 -0
- rakam_systems_core/ai_core/interfaces/loader.py +86 -0
- rakam_systems_core/ai_core/interfaces/reranker.py +10 -0
- rakam_systems_core/ai_core/interfaces/retriever.py +11 -0
- rakam_systems_core/ai_core/interfaces/tool.py +162 -0
- rakam_systems_core/ai_core/interfaces/tool_invoker.py +260 -0
- rakam_systems_core/ai_core/interfaces/tool_loader.py +374 -0
- rakam_systems_core/ai_core/interfaces/tool_registry.py +287 -0
- rakam_systems_core/ai_core/interfaces/vectorstore.py +37 -0
- rakam_systems_core/ai_core/mcp/README.md +545 -0
- rakam_systems_core/ai_core/mcp/__init__.py +0 -0
- rakam_systems_core/ai_core/mcp/mcp_server.py +334 -0
- rakam_systems_core/ai_core/tracking.py +602 -0
- rakam_systems_core/ai_core/vs_core.py +55 -0
- rakam_systems_core/ai_utils/__init__.py +16 -0
- rakam_systems_core/ai_utils/logging.py +126 -0
- rakam_systems_core/ai_utils/metrics.py +10 -0
- rakam_systems_core/ai_utils/s3.py +480 -0
- rakam_systems_core/ai_utils/tracing.py +5 -0
- rakam_systems_core-0.1.1rc7.dist-info/METADATA +162 -0
- rakam_systems_core-0.1.1rc7.dist-info/RECORD +34 -0
- rakam_systems_core-0.1.1rc7.dist-info/WHEEL +4 -0
|
@@ -0,0 +1,395 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Pydantic schemas for agent configuration and input/output tracking.
|
|
3
|
+
|
|
4
|
+
This module provides comprehensive schemas for:
|
|
5
|
+
1. Agent configuration (tools, models, prompts, skills)
|
|
6
|
+
2. Input/output tracking for agent methods
|
|
7
|
+
3. Evaluation data structures
|
|
8
|
+
"""
|
|
9
|
+
from __future__ import annotations
|
|
10
|
+
from typing import Any, Dict, List, Optional, Union
|
|
11
|
+
from pydantic import BaseModel, Field, validator
|
|
12
|
+
from datetime import datetime
|
|
13
|
+
from enum import Enum
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
# ============================================================================
|
|
17
|
+
# Configuration Schemas
|
|
18
|
+
# ============================================================================
|
|
19
|
+
|
|
20
|
+
class ToolMode(str, Enum):
|
|
21
|
+
"""Tool invocation modes."""
|
|
22
|
+
DIRECT = "direct"
|
|
23
|
+
MCP = "mcp"
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
class ToolConfigSchema(BaseModel):
|
|
27
|
+
"""Configuration schema for a single tool."""
|
|
28
|
+
model_config = {"use_enum_values": True}
|
|
29
|
+
|
|
30
|
+
name: str = Field(..., description="Unique name for the tool")
|
|
31
|
+
type: ToolMode = Field(..., description="Tool invocation mode (direct or mcp)")
|
|
32
|
+
description: str = Field(..., description="Human-readable description of the tool")
|
|
33
|
+
|
|
34
|
+
# Direct tool fields
|
|
35
|
+
module: Optional[str] = Field(None, description="Python module path for direct tools")
|
|
36
|
+
function: Optional[str] = Field(None, description="Function name for direct tools")
|
|
37
|
+
|
|
38
|
+
# MCP tool fields
|
|
39
|
+
mcp_server: Optional[str] = Field(None, description="MCP server name for MCP tools")
|
|
40
|
+
mcp_tool_name: Optional[str] = Field(None, description="Tool name on MCP server")
|
|
41
|
+
|
|
42
|
+
# Tool configuration (for tool-specific settings like model, etc.)
|
|
43
|
+
config: Optional[Dict[str, Any]] = Field(None, description="Tool-specific configuration (e.g., model settings)")
|
|
44
|
+
|
|
45
|
+
# JSON Schema for tool parameters (renamed from 'schema' to avoid shadowing)
|
|
46
|
+
json_schema: Optional[Dict[str, Any]] = Field(default_factory=dict, description="JSON Schema for tool parameters")
|
|
47
|
+
|
|
48
|
+
# Organization
|
|
49
|
+
category: Optional[str] = Field("general", description="Category for organizing tools")
|
|
50
|
+
tags: List[str] = Field(default_factory=list, description="Tags for filtering tools")
|
|
51
|
+
takes_ctx: bool = Field(False, description="Whether tool takes context as first argument")
|
|
52
|
+
|
|
53
|
+
@validator("module")
|
|
54
|
+
def validate_direct_tool(cls, v, values):
|
|
55
|
+
"""Validate that direct tools have required fields."""
|
|
56
|
+
if values.get("type") == ToolMode.DIRECT and not v:
|
|
57
|
+
raise ValueError("Direct tools must specify 'module'")
|
|
58
|
+
return v
|
|
59
|
+
|
|
60
|
+
@validator("mcp_server")
|
|
61
|
+
def validate_mcp_tool(cls, v, values):
|
|
62
|
+
"""Validate that MCP tools have required fields."""
|
|
63
|
+
if values.get("type") == ToolMode.MCP and not v:
|
|
64
|
+
raise ValueError("MCP tools must specify 'mcp_server'")
|
|
65
|
+
return v
|
|
66
|
+
|
|
67
|
+
|
|
68
|
+
class LLMGatewayConfigSchema(BaseModel):
|
|
69
|
+
"""Configuration schema for LLM Gateway settings."""
|
|
70
|
+
model_config = {"extra": "allow"} # Allow additional provider-specific fields
|
|
71
|
+
|
|
72
|
+
provider: str = Field(..., description="LLM provider (e.g., 'openai', 'mistral')")
|
|
73
|
+
model: str = Field(..., description="Model name (e.g., 'gpt-4o', 'mistral-large-latest')")
|
|
74
|
+
temperature: Optional[float] = Field(0.7, ge=0.0, le=2.0, description="Sampling temperature")
|
|
75
|
+
max_tokens: Optional[int] = Field(None, gt=0, description="Maximum tokens in response")
|
|
76
|
+
api_key: Optional[str] = Field(None, description="API key (if not using environment variable)")
|
|
77
|
+
|
|
78
|
+
# Provider-specific settings
|
|
79
|
+
base_url: Optional[str] = Field(None, description="Custom API base URL (OpenAI)")
|
|
80
|
+
organization: Optional[str] = Field(None, description="Organization ID (OpenAI)")
|
|
81
|
+
|
|
82
|
+
# Additional settings
|
|
83
|
+
extra_settings: Dict[str, Any] = Field(default_factory=dict, description="Additional provider settings")
|
|
84
|
+
|
|
85
|
+
|
|
86
|
+
class ModelConfigSchema(BaseModel):
|
|
87
|
+
"""Configuration schema for LLM model settings."""
|
|
88
|
+
model_config = {"extra": "allow"} # Allow additional fields
|
|
89
|
+
|
|
90
|
+
model: str = Field(..., description="Model identifier (e.g., 'openai:gpt-4o')")
|
|
91
|
+
temperature: Optional[float] = Field(None, ge=0.0, le=2.0, description="Sampling temperature")
|
|
92
|
+
max_tokens: Optional[int] = Field(None, gt=0, description="Maximum tokens in response")
|
|
93
|
+
parallel_tool_calls: bool = Field(True, description="Enable parallel tool execution")
|
|
94
|
+
extra_settings: Dict[str, Any] = Field(default_factory=dict, description="Additional model settings")
|
|
95
|
+
|
|
96
|
+
|
|
97
|
+
class PromptConfigSchema(BaseModel):
|
|
98
|
+
"""Configuration schema for system prompts and skill sets."""
|
|
99
|
+
model_config = {"extra": "allow"}
|
|
100
|
+
|
|
101
|
+
name: str = Field(..., description="Prompt identifier")
|
|
102
|
+
system_prompt: str = Field(..., description="System prompt text")
|
|
103
|
+
description: Optional[str] = Field(None, description="Description of this prompt/skill")
|
|
104
|
+
tags: List[str] = Field(default_factory=list, description="Tags for categorizing prompts")
|
|
105
|
+
|
|
106
|
+
# Skills can be structured like Anthropic's approach
|
|
107
|
+
skills: List[str] = Field(default_factory=list, description="List of skills/capabilities")
|
|
108
|
+
examples: List[Dict[str, str]] = Field(default_factory=list, description="Example interactions")
|
|
109
|
+
|
|
110
|
+
|
|
111
|
+
class OutputFieldSchema(BaseModel):
|
|
112
|
+
"""Configuration schema for a single field in the structured output."""
|
|
113
|
+
model_config = {"extra": "allow"}
|
|
114
|
+
|
|
115
|
+
type: str = Field(
|
|
116
|
+
...,
|
|
117
|
+
description="Field type: 'str', 'int', 'float', 'bool', 'list', 'dict'"
|
|
118
|
+
)
|
|
119
|
+
description: str = Field(..., description="Description of the field (used in LLM prompt)")
|
|
120
|
+
default: Optional[Any] = Field(None, description="Default value for the field")
|
|
121
|
+
default_factory: Optional[str] = Field(
|
|
122
|
+
None,
|
|
123
|
+
description="Default factory: 'list' for [], 'dict' for {}"
|
|
124
|
+
)
|
|
125
|
+
required: bool = Field(True, description="Whether the field is required")
|
|
126
|
+
|
|
127
|
+
|
|
128
|
+
class OutputTypeSchema(BaseModel):
|
|
129
|
+
"""Configuration schema for structured output type defined inline in YAML.
|
|
130
|
+
|
|
131
|
+
This allows defining the output structure directly in the configuration
|
|
132
|
+
instead of referencing a Python class.
|
|
133
|
+
|
|
134
|
+
Example YAML:
|
|
135
|
+
output_type:
|
|
136
|
+
name: "SQLAgentOutput"
|
|
137
|
+
description: "Output for SQL agent"
|
|
138
|
+
fields:
|
|
139
|
+
answer:
|
|
140
|
+
type: str
|
|
141
|
+
description: "The answer to the user's question"
|
|
142
|
+
sql_query:
|
|
143
|
+
type: str
|
|
144
|
+
description: "The SQL query used"
|
|
145
|
+
default: ""
|
|
146
|
+
show_chart:
|
|
147
|
+
type: bool
|
|
148
|
+
description: "Whether to show a chart"
|
|
149
|
+
default: false
|
|
150
|
+
chart_config:
|
|
151
|
+
type: dict
|
|
152
|
+
description: "Chart configuration"
|
|
153
|
+
default_factory: dict
|
|
154
|
+
"""
|
|
155
|
+
model_config = {"extra": "allow"}
|
|
156
|
+
|
|
157
|
+
name: str = Field(..., description="Name for the generated Pydantic model")
|
|
158
|
+
description: Optional[str] = Field(None, description="Description of the output model")
|
|
159
|
+
fields: Dict[str, OutputFieldSchema] = Field(
|
|
160
|
+
...,
|
|
161
|
+
description="Dictionary of field name to field configuration"
|
|
162
|
+
)
|
|
163
|
+
|
|
164
|
+
|
|
165
|
+
class AgentConfigSchema(BaseModel):
|
|
166
|
+
"""Complete agent configuration schema."""
|
|
167
|
+
model_config = {"extra": "allow"}
|
|
168
|
+
|
|
169
|
+
name: str = Field(..., description="Agent name/identifier")
|
|
170
|
+
description: Optional[str] = Field(None, description="Agent description")
|
|
171
|
+
|
|
172
|
+
# Model configuration
|
|
173
|
+
llm_config: ModelConfigSchema = Field(..., description="LLM model configuration", alias="model_config")
|
|
174
|
+
|
|
175
|
+
# Prompt configuration
|
|
176
|
+
prompt_config: Union[str, PromptConfigSchema] = Field(
|
|
177
|
+
...,
|
|
178
|
+
description="Prompt configuration (name reference or full config)"
|
|
179
|
+
)
|
|
180
|
+
|
|
181
|
+
# Tools configuration
|
|
182
|
+
tools: List[Union[str, ToolConfigSchema]] = Field(
|
|
183
|
+
default_factory=list,
|
|
184
|
+
description="List of tools (names or full configs)"
|
|
185
|
+
)
|
|
186
|
+
|
|
187
|
+
# Dependencies
|
|
188
|
+
deps_type: Optional[str] = Field(None, description="Fully qualified class name for dependencies")
|
|
189
|
+
|
|
190
|
+
# Structured output type (Pydantic model)
|
|
191
|
+
# Can be either:
|
|
192
|
+
# 1. A string: fully qualified class name (e.g., 'myapp.models.AgentOutput')
|
|
193
|
+
# 2. An OutputTypeSchema: inline definition of the output structure
|
|
194
|
+
output_type: Optional[Union[str, OutputTypeSchema]] = Field(
|
|
195
|
+
None,
|
|
196
|
+
description="Structured output type: class path string or inline schema definition"
|
|
197
|
+
)
|
|
198
|
+
|
|
199
|
+
# Tracking and evaluation
|
|
200
|
+
enable_tracking: bool = Field(True, description="Enable input/output tracking")
|
|
201
|
+
tracking_output_dir: str = Field("./agent_tracking", description="Directory for tracking outputs")
|
|
202
|
+
|
|
203
|
+
# Additional settings
|
|
204
|
+
stateful: bool = Field(False, description="Whether agent maintains state")
|
|
205
|
+
metadata: Dict[str, Any] = Field(default_factory=dict, description="Additional metadata")
|
|
206
|
+
|
|
207
|
+
|
|
208
|
+
class ConfigFileSchema(BaseModel):
|
|
209
|
+
"""Root configuration file schema."""
|
|
210
|
+
model_config = {"extra": "allow"}
|
|
211
|
+
|
|
212
|
+
version: str = Field("1.0", description="Configuration file version")
|
|
213
|
+
|
|
214
|
+
# Global settings
|
|
215
|
+
global_settings: Dict[str, Any] = Field(default_factory=dict, description="Global settings")
|
|
216
|
+
|
|
217
|
+
# LLM Gateway configurations
|
|
218
|
+
llm_gateways: Dict[str, LLMGatewayConfigSchema] = Field(
|
|
219
|
+
default_factory=dict,
|
|
220
|
+
description="Library of LLM gateway configurations"
|
|
221
|
+
)
|
|
222
|
+
|
|
223
|
+
# Prompt library - reusable prompts
|
|
224
|
+
prompts: Dict[str, PromptConfigSchema] = Field(
|
|
225
|
+
default_factory=dict,
|
|
226
|
+
description="Library of reusable prompts"
|
|
227
|
+
)
|
|
228
|
+
|
|
229
|
+
# Tool library - reusable tools
|
|
230
|
+
tools: Dict[str, ToolConfigSchema] = Field(
|
|
231
|
+
default_factory=dict,
|
|
232
|
+
description="Library of reusable tools"
|
|
233
|
+
)
|
|
234
|
+
|
|
235
|
+
# Agents
|
|
236
|
+
agents: Dict[str, AgentConfigSchema] = Field(
|
|
237
|
+
default_factory=dict,
|
|
238
|
+
description="Agent configurations"
|
|
239
|
+
)
|
|
240
|
+
|
|
241
|
+
|
|
242
|
+
# ============================================================================
|
|
243
|
+
# Input/Output Tracking Schemas
|
|
244
|
+
# ============================================================================
|
|
245
|
+
|
|
246
|
+
class MethodInputSchema(BaseModel):
|
|
247
|
+
"""Schema for capturing method input data."""
|
|
248
|
+
model_config = {
|
|
249
|
+
"arbitrary_types_allowed": True,
|
|
250
|
+
"json_encoders": {datetime: lambda v: v.isoformat()}
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
timestamp: datetime = Field(default_factory=datetime.now, description="When method was called")
|
|
254
|
+
method_name: str = Field(..., description="Name of the method")
|
|
255
|
+
agent_name: str = Field(..., description="Name of the agent")
|
|
256
|
+
|
|
257
|
+
# Input data
|
|
258
|
+
input_text: Optional[str] = Field(None, description="Input text if applicable")
|
|
259
|
+
args: List[Any] = Field(default_factory=list, description="Positional arguments")
|
|
260
|
+
kwargs: Dict[str, Any] = Field(default_factory=dict, description="Keyword arguments")
|
|
261
|
+
|
|
262
|
+
# Context
|
|
263
|
+
context: Dict[str, Any] = Field(default_factory=dict, description="Additional context")
|
|
264
|
+
|
|
265
|
+
# Tracking metadata
|
|
266
|
+
call_id: str = Field(..., description="Unique identifier for this call")
|
|
267
|
+
parent_call_id: Optional[str] = Field(None, description="Parent call ID if nested")
|
|
268
|
+
|
|
269
|
+
|
|
270
|
+
class MethodOutputSchema(BaseModel):
|
|
271
|
+
"""Schema for capturing method output data."""
|
|
272
|
+
model_config = {
|
|
273
|
+
"arbitrary_types_allowed": True,
|
|
274
|
+
"json_encoders": {datetime: lambda v: v.isoformat()}
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
timestamp: datetime = Field(default_factory=datetime.now, description="When method completed")
|
|
278
|
+
method_name: str = Field(..., description="Name of the method")
|
|
279
|
+
agent_name: str = Field(..., description="Name of the agent")
|
|
280
|
+
|
|
281
|
+
# Output data
|
|
282
|
+
output_text: Optional[str] = Field(None, description="Output text if applicable")
|
|
283
|
+
result: Any = Field(None, description="Method return value")
|
|
284
|
+
|
|
285
|
+
# Performance metrics
|
|
286
|
+
duration_seconds: float = Field(..., description="Execution time in seconds")
|
|
287
|
+
|
|
288
|
+
# Status
|
|
289
|
+
success: bool = Field(..., description="Whether method executed successfully")
|
|
290
|
+
error: Optional[str] = Field(None, description="Error message if failed")
|
|
291
|
+
|
|
292
|
+
# Metadata
|
|
293
|
+
metadata: Dict[str, Any] = Field(default_factory=dict, description="Additional metadata")
|
|
294
|
+
|
|
295
|
+
# Tracking metadata
|
|
296
|
+
call_id: str = Field(..., description="Unique identifier matching input")
|
|
297
|
+
parent_call_id: Optional[str] = Field(None, description="Parent call ID if nested")
|
|
298
|
+
|
|
299
|
+
|
|
300
|
+
class MethodCallRecordSchema(BaseModel):
|
|
301
|
+
"""Complete record of a method call (input + output)."""
|
|
302
|
+
model_config = {
|
|
303
|
+
"arbitrary_types_allowed": True,
|
|
304
|
+
"json_encoders": {datetime: lambda v: v.isoformat()}
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
call_id: str = Field(..., description="Unique identifier for this call")
|
|
308
|
+
agent_name: str = Field(..., description="Name of the agent")
|
|
309
|
+
method_name: str = Field(..., description="Name of the method")
|
|
310
|
+
|
|
311
|
+
# Input/Output
|
|
312
|
+
input_data: MethodInputSchema = Field(..., description="Input data")
|
|
313
|
+
output_data: MethodOutputSchema = Field(..., description="Output data")
|
|
314
|
+
|
|
315
|
+
# Timing
|
|
316
|
+
started_at: datetime = Field(..., description="When call started")
|
|
317
|
+
completed_at: datetime = Field(..., description="When call completed")
|
|
318
|
+
duration_seconds: float = Field(..., description="Total execution time")
|
|
319
|
+
|
|
320
|
+
# Evaluation fields (to be filled later)
|
|
321
|
+
evaluation_score: Optional[float] = Field(None, description="Evaluation score if available")
|
|
322
|
+
evaluation_notes: Optional[str] = Field(None, description="Evaluation notes")
|
|
323
|
+
|
|
324
|
+
|
|
325
|
+
class TrackingSessionSchema(BaseModel):
|
|
326
|
+
"""Schema for a tracking session containing multiple calls."""
|
|
327
|
+
model_config = {
|
|
328
|
+
"arbitrary_types_allowed": True,
|
|
329
|
+
"json_encoders": {datetime: lambda v: v.isoformat()}
|
|
330
|
+
}
|
|
331
|
+
|
|
332
|
+
session_id: str = Field(..., description="Unique session identifier")
|
|
333
|
+
agent_name: str = Field(..., description="Name of the agent")
|
|
334
|
+
started_at: datetime = Field(default_factory=datetime.now, description="Session start time")
|
|
335
|
+
ended_at: Optional[datetime] = Field(None, description="Session end time")
|
|
336
|
+
|
|
337
|
+
# Calls in this session
|
|
338
|
+
calls: List[MethodCallRecordSchema] = Field(default_factory=list, description="Method calls in session")
|
|
339
|
+
|
|
340
|
+
# Session metadata
|
|
341
|
+
metadata: Dict[str, Any] = Field(default_factory=dict, description="Session metadata")
|
|
342
|
+
|
|
343
|
+
# Summary statistics
|
|
344
|
+
total_calls: int = Field(0, description="Total number of calls")
|
|
345
|
+
successful_calls: int = Field(0, description="Number of successful calls")
|
|
346
|
+
failed_calls: int = Field(0, description="Number of failed calls")
|
|
347
|
+
total_duration: float = Field(0.0, description="Total execution time")
|
|
348
|
+
|
|
349
|
+
def add_call(self, call_record: MethodCallRecordSchema) -> None:
|
|
350
|
+
"""Add a call record and update statistics."""
|
|
351
|
+
self.calls.append(call_record)
|
|
352
|
+
self.total_calls += 1
|
|
353
|
+
if call_record.output_data.success:
|
|
354
|
+
self.successful_calls += 1
|
|
355
|
+
else:
|
|
356
|
+
self.failed_calls += 1
|
|
357
|
+
self.total_duration += call_record.duration_seconds
|
|
358
|
+
|
|
359
|
+
def end_session(self) -> None:
|
|
360
|
+
"""Mark the session as ended."""
|
|
361
|
+
self.ended_at = datetime.now()
|
|
362
|
+
|
|
363
|
+
|
|
364
|
+
# ============================================================================
|
|
365
|
+
# Evaluation Schemas
|
|
366
|
+
# ============================================================================
|
|
367
|
+
|
|
368
|
+
class EvaluationCriteriaSchema(BaseModel):
|
|
369
|
+
"""Schema for evaluation criteria."""
|
|
370
|
+
name: str = Field(..., description="Criterion name")
|
|
371
|
+
description: str = Field(..., description="What this criterion measures")
|
|
372
|
+
weight: float = Field(1.0, ge=0.0, description="Weight for this criterion")
|
|
373
|
+
min_score: float = Field(0.0, description="Minimum score")
|
|
374
|
+
max_score: float = Field(1.0, description="Maximum score")
|
|
375
|
+
|
|
376
|
+
|
|
377
|
+
class EvaluationResultSchema(BaseModel):
|
|
378
|
+
"""Schema for evaluation results."""
|
|
379
|
+
model_config = {"json_encoders": {datetime: lambda v: v.isoformat()}}
|
|
380
|
+
|
|
381
|
+
call_id: str = Field(..., description="Call ID being evaluated")
|
|
382
|
+
evaluated_at: datetime = Field(default_factory=datetime.now, description="When evaluation occurred")
|
|
383
|
+
evaluator: str = Field(..., description="Who/what performed the evaluation")
|
|
384
|
+
|
|
385
|
+
# Scores per criterion
|
|
386
|
+
scores: Dict[str, float] = Field(default_factory=dict, description="Scores by criterion")
|
|
387
|
+
|
|
388
|
+
# Overall
|
|
389
|
+
overall_score: float = Field(..., description="Overall weighted score")
|
|
390
|
+
passed: bool = Field(..., description="Whether evaluation passed")
|
|
391
|
+
|
|
392
|
+
# Feedback
|
|
393
|
+
feedback: Optional[str] = Field(None, description="Textual feedback")
|
|
394
|
+
suggestions: List[str] = Field(default_factory=list, description="Improvement suggestions")
|
|
395
|
+
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
# Interface package
|
|
2
|
+
from .agent import AgentComponent, AgentInput, AgentOutput, ModelSettings
|
|
3
|
+
from .chat_history import ChatHistoryComponent
|
|
4
|
+
from .tool import ToolComponent
|
|
5
|
+
from .tool_registry import ToolRegistry, ToolMetadata, ToolMode
|
|
6
|
+
from .tool_invoker import ToolInvoker, ToolInvocationError, ToolNotFoundError, MCPServerNotFoundError
|
|
7
|
+
from .tool_loader import ToolLoader, ToolLoadError
|
|
8
|
+
from ..vs_core import Node, NodeMetadata, VSFile
|
|
9
|
+
|
|
10
|
+
__all__ = [
|
|
11
|
+
"AgentComponent",
|
|
12
|
+
"AgentInput",
|
|
13
|
+
"AgentOutput",
|
|
14
|
+
"ModelSettings",
|
|
15
|
+
"ChatHistoryComponent",
|
|
16
|
+
"ToolComponent",
|
|
17
|
+
"ToolRegistry",
|
|
18
|
+
"ToolMetadata",
|
|
19
|
+
"ToolMode",
|
|
20
|
+
"ToolInvoker",
|
|
21
|
+
"ToolInvocationError",
|
|
22
|
+
"ToolNotFoundError",
|
|
23
|
+
"MCPServerNotFoundError",
|
|
24
|
+
"ToolLoader",
|
|
25
|
+
"ToolLoadError",
|
|
26
|
+
# Core data structures
|
|
27
|
+
"Node",
|
|
28
|
+
"NodeMetadata",
|
|
29
|
+
"VSFile",
|
|
30
|
+
]
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
from abc import ABC, abstractmethod
|
|
3
|
+
from typing import Any, Dict, Generic, Iterator, Optional, List, Type, TypeVar, Union, AsyncIterator
|
|
4
|
+
from ..base import BaseComponent
|
|
5
|
+
|
|
6
|
+
# Type variable for structured output
|
|
7
|
+
OutputT = TypeVar('OutputT')
|
|
8
|
+
|
|
9
|
+
class AgentInput:
|
|
10
|
+
"""Simple DTO for agent inputs (no external deps)."""
|
|
11
|
+
def __init__(self, input_text: str, context: Optional[Dict[str, Any]] = None) -> None:
|
|
12
|
+
self.input_text = input_text
|
|
13
|
+
self.context = context or {}
|
|
14
|
+
|
|
15
|
+
class AgentOutput(Generic[OutputT]):
|
|
16
|
+
"""Simple DTO for agent outputs.
|
|
17
|
+
|
|
18
|
+
Supports both simple string outputs and structured outputs via output_type.
|
|
19
|
+
When output_type is used, access the structured data via the `output` attribute.
|
|
20
|
+
The `output_text` attribute provides a string representation for backward compatibility.
|
|
21
|
+
"""
|
|
22
|
+
def __init__(
|
|
23
|
+
self,
|
|
24
|
+
output_text: str,
|
|
25
|
+
metadata: Optional[Dict[str, Any]] = None,
|
|
26
|
+
output: Optional[OutputT] = None
|
|
27
|
+
) -> None:
|
|
28
|
+
self.output_text = output_text
|
|
29
|
+
self.metadata = metadata or {}
|
|
30
|
+
self.output = output # Structured output when output_type is used
|
|
31
|
+
|
|
32
|
+
class ModelSettings:
|
|
33
|
+
"""Settings for LLM model behavior."""
|
|
34
|
+
def __init__(
|
|
35
|
+
self,
|
|
36
|
+
parallel_tool_calls: bool = True,
|
|
37
|
+
temperature: Optional[float] = None,
|
|
38
|
+
max_tokens: Optional[int] = None,
|
|
39
|
+
**kwargs: Any
|
|
40
|
+
) -> None:
|
|
41
|
+
self.parallel_tool_calls = parallel_tool_calls
|
|
42
|
+
self.temperature = temperature
|
|
43
|
+
self.max_tokens = max_tokens
|
|
44
|
+
self.extra_settings = kwargs
|
|
45
|
+
|
|
46
|
+
class AgentComponent(BaseComponent, ABC):
|
|
47
|
+
"""Abstract agent interface with optional streaming and async support."""
|
|
48
|
+
def __init__(
|
|
49
|
+
self,
|
|
50
|
+
name: str,
|
|
51
|
+
config: Optional[Dict[str, Any]] = None,
|
|
52
|
+
model: Optional[str] = None,
|
|
53
|
+
deps_type: Optional[Type[Any]] = None,
|
|
54
|
+
output_type: Optional[Type[Any]] = None,
|
|
55
|
+
system_prompt: Optional[str] = None,
|
|
56
|
+
tools: Optional[List[Any]] = None,
|
|
57
|
+
) -> None:
|
|
58
|
+
super().__init__(name, config)
|
|
59
|
+
self.stateful: bool = bool((config or {}).get("stateful", False))
|
|
60
|
+
self.model = model or (config or {}).get("model", "openai:gpt-4")
|
|
61
|
+
self.deps_type = deps_type
|
|
62
|
+
self.output_type = output_type # Pydantic model for structured output
|
|
63
|
+
self.system_prompt = system_prompt or (config or {}).get("system_prompt", "")
|
|
64
|
+
self.tools = tools or []
|
|
65
|
+
|
|
66
|
+
@abstractmethod
|
|
67
|
+
def run(self, input_data: Union[str, AgentInput], deps: Optional[Any] = None, model_settings: Optional[ModelSettings] = None) -> AgentOutput:
|
|
68
|
+
"""Synchronous run interface."""
|
|
69
|
+
raise NotImplementedError
|
|
70
|
+
|
|
71
|
+
async def arun(self, input_data: Union[str, AgentInput], deps: Optional[Any] = None, model_settings: Optional[ModelSettings] = None) -> AgentOutput:
|
|
72
|
+
"""Async run interface - override for true async support."""
|
|
73
|
+
raise NotImplementedError
|
|
74
|
+
|
|
75
|
+
def stream(self, input_data: Union[str, AgentInput], deps: Optional[Any] = None, model_settings: Optional[ModelSettings] = None) -> Iterator[str]:
|
|
76
|
+
"""Optional streaming interface: by default yields the final text once."""
|
|
77
|
+
out = self.run(input_data, deps=deps, model_settings=model_settings)
|
|
78
|
+
yield out.output_text
|
|
79
|
+
|
|
80
|
+
async def astream(self, input_data: Union[str, AgentInput], deps: Optional[Any] = None, model_settings: Optional[ModelSettings] = None) -> AsyncIterator[str]:
|
|
81
|
+
"""Async streaming interface."""
|
|
82
|
+
out = await self.arun(input_data, deps=deps, model_settings=model_settings)
|
|
83
|
+
yield out.output_text
|
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
"""Chat History Interface.
|
|
2
|
+
|
|
3
|
+
This module defines the abstract interface for chat history management components.
|
|
4
|
+
Implementations can use different storage backends (JSON, database, etc.).
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
from __future__ import annotations
|
|
8
|
+
from abc import ABC, abstractmethod
|
|
9
|
+
from typing import Any, Dict, List, Optional
|
|
10
|
+
from ..base import BaseComponent
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
class ChatHistoryComponent(BaseComponent, ABC):
|
|
14
|
+
"""Abstract interface for chat history management.
|
|
15
|
+
|
|
16
|
+
This component manages conversation history across multiple chat sessions,
|
|
17
|
+
providing CRUD operations for messages and chat sessions.
|
|
18
|
+
|
|
19
|
+
Implementations should handle:
|
|
20
|
+
- Storing and retrieving messages by chat_id
|
|
21
|
+
- Persistence of chat history (file, database, etc.)
|
|
22
|
+
- Optional: message formatting for display
|
|
23
|
+
"""
|
|
24
|
+
|
|
25
|
+
def __init__(
|
|
26
|
+
self,
|
|
27
|
+
name: str = "chat_history",
|
|
28
|
+
config: Optional[Dict[str, Any]] = None,
|
|
29
|
+
) -> None:
|
|
30
|
+
"""Initialize the chat history component.
|
|
31
|
+
|
|
32
|
+
Args:
|
|
33
|
+
name: Component name for identification.
|
|
34
|
+
config: Configuration dictionary with storage-specific settings.
|
|
35
|
+
"""
|
|
36
|
+
super().__init__(name, config)
|
|
37
|
+
|
|
38
|
+
def run(self, *args: Any, **kwargs: Any) -> Any:
|
|
39
|
+
"""Default run method - delegates to get_chat_history if chat_id provided."""
|
|
40
|
+
if args and isinstance(args[0], str):
|
|
41
|
+
return self.get_chat_history(args[0])
|
|
42
|
+
raise ValueError("run() requires a chat_id as the first argument")
|
|
43
|
+
|
|
44
|
+
@abstractmethod
|
|
45
|
+
def add_message(self, chat_id: str, message: Dict[str, Any]) -> None:
|
|
46
|
+
"""Add a single message to a chat session.
|
|
47
|
+
|
|
48
|
+
Args:
|
|
49
|
+
chat_id: Unique identifier for the chat session.
|
|
50
|
+
message: Message object containing content, role, timestamp, etc.
|
|
51
|
+
"""
|
|
52
|
+
raise NotImplementedError
|
|
53
|
+
|
|
54
|
+
@abstractmethod
|
|
55
|
+
def set_messages(self, chat_id: str, messages: List[Dict[str, Any]]) -> None:
|
|
56
|
+
"""Set/replace all messages for a chat session.
|
|
57
|
+
|
|
58
|
+
Args:
|
|
59
|
+
chat_id: Unique identifier for the chat session.
|
|
60
|
+
messages: List of message objects to store.
|
|
61
|
+
"""
|
|
62
|
+
raise NotImplementedError
|
|
63
|
+
|
|
64
|
+
@abstractmethod
|
|
65
|
+
def get_chat_history(self, chat_id: str) -> List[Dict[str, Any]]:
|
|
66
|
+
"""Retrieve all messages for a chat session.
|
|
67
|
+
|
|
68
|
+
Args:
|
|
69
|
+
chat_id: Unique identifier for the chat session.
|
|
70
|
+
|
|
71
|
+
Returns:
|
|
72
|
+
List of message objects, or empty list if chat doesn't exist.
|
|
73
|
+
"""
|
|
74
|
+
raise NotImplementedError
|
|
75
|
+
|
|
76
|
+
@abstractmethod
|
|
77
|
+
def get_all_chat_ids(self) -> List[str]:
|
|
78
|
+
"""Get all chat IDs currently stored.
|
|
79
|
+
|
|
80
|
+
Returns:
|
|
81
|
+
List of all chat session identifiers.
|
|
82
|
+
"""
|
|
83
|
+
raise NotImplementedError
|
|
84
|
+
|
|
85
|
+
@abstractmethod
|
|
86
|
+
def delete_chat_history(self, chat_id: str) -> bool:
|
|
87
|
+
"""Delete all messages for a chat session.
|
|
88
|
+
|
|
89
|
+
Args:
|
|
90
|
+
chat_id: Unique identifier for the chat session to delete.
|
|
91
|
+
|
|
92
|
+
Returns:
|
|
93
|
+
True if deletion was successful, False if chat_id didn't exist.
|
|
94
|
+
"""
|
|
95
|
+
raise NotImplementedError
|
|
96
|
+
|
|
97
|
+
def clear_all(self) -> None:
|
|
98
|
+
"""Delete all chat histories. Override for efficient implementation."""
|
|
99
|
+
for chat_id in self.get_all_chat_ids():
|
|
100
|
+
self.delete_chat_history(chat_id)
|
|
101
|
+
|
|
102
|
+
def get_message_count(self, chat_id: str) -> int:
|
|
103
|
+
"""Get the number of messages in a chat session.
|
|
104
|
+
|
|
105
|
+
Args:
|
|
106
|
+
chat_id: Unique identifier for the chat session.
|
|
107
|
+
|
|
108
|
+
Returns:
|
|
109
|
+
Number of messages, or 0 if chat doesn't exist.
|
|
110
|
+
"""
|
|
111
|
+
return len(self.get_chat_history(chat_id))
|
|
112
|
+
|
|
113
|
+
def chat_exists(self, chat_id: str) -> bool:
|
|
114
|
+
"""Check if a chat session exists.
|
|
115
|
+
|
|
116
|
+
Args:
|
|
117
|
+
chat_id: Unique identifier for the chat session.
|
|
118
|
+
|
|
119
|
+
Returns:
|
|
120
|
+
True if chat exists, False otherwise.
|
|
121
|
+
"""
|
|
122
|
+
return chat_id in self.get_all_chat_ids()
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
from abc import ABC, abstractmethod
|
|
3
|
+
from typing import List
|
|
4
|
+
from ..base import BaseComponent
|
|
5
|
+
|
|
6
|
+
class Chunker(BaseComponent, ABC):
|
|
7
|
+
@abstractmethod
|
|
8
|
+
def run(self, documents: List[str]) -> List[str]:
|
|
9
|
+
"""Split documents into smaller chunks.
|
|
10
|
+
Keep pure string IO to avoid extra dependencies."""
|
|
11
|
+
raise NotImplementedError
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
from abc import ABC, abstractmethod
|
|
3
|
+
from typing import List
|
|
4
|
+
from ..base import BaseComponent
|
|
5
|
+
|
|
6
|
+
class EmbeddingModel(BaseComponent, ABC):
|
|
7
|
+
@abstractmethod
|
|
8
|
+
def run(self, texts: List[str]) -> List[List[float]]:
|
|
9
|
+
"""Return one vector per input text."""
|
|
10
|
+
raise NotImplementedError
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
from abc import ABC, abstractmethod
|
|
3
|
+
from typing import Any, Dict, List
|
|
4
|
+
from ..base import BaseComponent
|
|
5
|
+
|
|
6
|
+
class Indexer(BaseComponent, ABC):
|
|
7
|
+
@abstractmethod
|
|
8
|
+
def run(self, documents: List[str], embeddings: List[List[float]]) -> Any:
|
|
9
|
+
"""Index documents + embeddings into a backing store."""
|
|
10
|
+
raise NotImplementedError
|