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.
Files changed (34) hide show
  1. rakam_systems_core/__init__.py +41 -0
  2. rakam_systems_core/ai_core/__init__.py +68 -0
  3. rakam_systems_core/ai_core/base.py +142 -0
  4. rakam_systems_core/ai_core/config.py +12 -0
  5. rakam_systems_core/ai_core/config_loader.py +580 -0
  6. rakam_systems_core/ai_core/config_schema.py +395 -0
  7. rakam_systems_core/ai_core/interfaces/__init__.py +30 -0
  8. rakam_systems_core/ai_core/interfaces/agent.py +83 -0
  9. rakam_systems_core/ai_core/interfaces/chat_history.py +122 -0
  10. rakam_systems_core/ai_core/interfaces/chunker.py +11 -0
  11. rakam_systems_core/ai_core/interfaces/embedding_model.py +10 -0
  12. rakam_systems_core/ai_core/interfaces/indexer.py +10 -0
  13. rakam_systems_core/ai_core/interfaces/llm_gateway.py +139 -0
  14. rakam_systems_core/ai_core/interfaces/loader.py +86 -0
  15. rakam_systems_core/ai_core/interfaces/reranker.py +10 -0
  16. rakam_systems_core/ai_core/interfaces/retriever.py +11 -0
  17. rakam_systems_core/ai_core/interfaces/tool.py +162 -0
  18. rakam_systems_core/ai_core/interfaces/tool_invoker.py +260 -0
  19. rakam_systems_core/ai_core/interfaces/tool_loader.py +374 -0
  20. rakam_systems_core/ai_core/interfaces/tool_registry.py +287 -0
  21. rakam_systems_core/ai_core/interfaces/vectorstore.py +37 -0
  22. rakam_systems_core/ai_core/mcp/README.md +545 -0
  23. rakam_systems_core/ai_core/mcp/__init__.py +0 -0
  24. rakam_systems_core/ai_core/mcp/mcp_server.py +334 -0
  25. rakam_systems_core/ai_core/tracking.py +602 -0
  26. rakam_systems_core/ai_core/vs_core.py +55 -0
  27. rakam_systems_core/ai_utils/__init__.py +16 -0
  28. rakam_systems_core/ai_utils/logging.py +126 -0
  29. rakam_systems_core/ai_utils/metrics.py +10 -0
  30. rakam_systems_core/ai_utils/s3.py +480 -0
  31. rakam_systems_core/ai_utils/tracing.py +5 -0
  32. rakam_systems_core-0.1.1rc7.dist-info/METADATA +162 -0
  33. rakam_systems_core-0.1.1rc7.dist-info/RECORD +34 -0
  34. 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