flatagents 0.4.1__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.
flatagents/__init__.py ADDED
@@ -0,0 +1,136 @@
1
+ __version__ = "0.4.1"
2
+
3
+ from .baseagent import (
4
+ # Base agent (abstract, for multi-step agents)
5
+ FlatAgent as BaseFlatAgent,
6
+ # LLM Backends
7
+ LLMBackend,
8
+ LiteLLMBackend,
9
+ AISuiteBackend,
10
+ # Extractors
11
+ Extractor,
12
+ FreeExtractor,
13
+ FreeThinkingExtractor,
14
+ StructuredExtractor,
15
+ ToolsExtractor,
16
+ RegexExtractor,
17
+ # MCP Types
18
+ MCPToolProvider,
19
+ ToolCall,
20
+ AgentResponse,
21
+ )
22
+ from .flatagent import FlatAgent
23
+ from .flatmachine import FlatMachine
24
+ from .hooks import (
25
+ MachineHooks,
26
+ LoggingHooks,
27
+ MetricsHooks,
28
+ CompositeHooks,
29
+ )
30
+ from .expressions import get_expression_engine, ExpressionEngine
31
+ from .execution import (
32
+ ExecutionType,
33
+ DefaultExecution,
34
+ ParallelExecution,
35
+ RetryExecution,
36
+ MDAPVotingExecution,
37
+ get_execution_type,
38
+ )
39
+ from .validation import (
40
+ validate_flatagent_config,
41
+ validate_flatmachine_config,
42
+ get_flatagent_schema,
43
+ get_flatmachine_schema,
44
+ get_asset,
45
+ ValidationWarning,
46
+ )
47
+ from .monitoring import (
48
+ setup_logging,
49
+ get_logger,
50
+ get_meter,
51
+ AgentMonitor,
52
+ track_operation,
53
+ )
54
+ from .backends import (
55
+ ResultBackend,
56
+ InMemoryResultBackend,
57
+ LaunchIntent,
58
+ make_uri,
59
+ parse_uri,
60
+ get_default_result_backend,
61
+ reset_default_result_backend,
62
+ )
63
+ from .persistence import (
64
+ PersistenceBackend,
65
+ LocalFileBackend,
66
+ MemoryBackend,
67
+ CheckpointManager,
68
+ MachineSnapshot,
69
+ )
70
+
71
+ __all__ = [
72
+ "__version__",
73
+ # Main agent class
74
+ "FlatAgent",
75
+ # Base agent for custom multi-step agents
76
+ "BaseFlatAgent",
77
+ # State machine orchestration
78
+ "FlatMachine",
79
+ # Machine hooks
80
+ "MachineHooks",
81
+ "LoggingHooks",
82
+ "MetricsHooks",
83
+ "CompositeHooks",
84
+ # Expression engines
85
+ "ExpressionEngine",
86
+ "get_expression_engine",
87
+ # Execution types
88
+ "ExecutionType",
89
+ "DefaultExecution",
90
+ "ParallelExecution",
91
+ "RetryExecution",
92
+ "MDAPVotingExecution",
93
+ "get_execution_type",
94
+ # LLM Backends
95
+ "LLMBackend",
96
+ "LiteLLMBackend",
97
+ "AISuiteBackend",
98
+ # Extractors
99
+ "Extractor",
100
+ "FreeExtractor",
101
+ "FreeThinkingExtractor",
102
+ "StructuredExtractor",
103
+ "ToolsExtractor",
104
+ "RegexExtractor",
105
+ # MCP Types
106
+ "MCPToolProvider",
107
+ "ToolCall",
108
+ "AgentResponse",
109
+ # Validation
110
+ "validate_flatagent_config",
111
+ "validate_flatmachine_config",
112
+ "get_flatagent_schema",
113
+ "get_flatmachine_schema",
114
+ "get_asset",
115
+ "ValidationWarning",
116
+ # Monitoring & Observability
117
+ "setup_logging",
118
+ "get_logger",
119
+ "get_meter",
120
+ "AgentMonitor",
121
+ "track_operation",
122
+ # Result Backends (v0.4.0)
123
+ "ResultBackend",
124
+ "InMemoryResultBackend",
125
+ "LaunchIntent",
126
+ "make_uri",
127
+ "parse_uri",
128
+ "get_default_result_backend",
129
+ "reset_default_result_backend",
130
+ # Persistence Backends
131
+ "PersistenceBackend",
132
+ "LocalFileBackend",
133
+ "MemoryBackend",
134
+ "CheckpointManager",
135
+ "MachineSnapshot",
136
+ ]
flatagents/actions.py ADDED
@@ -0,0 +1,239 @@
1
+ from abc import ABC, abstractmethod
2
+ from typing import Any, Dict, Optional, TYPE_CHECKING
3
+ import logging
4
+
5
+ if TYPE_CHECKING:
6
+ from .flatmachine import FlatMachine
7
+
8
+ logger = logging.getLogger(__name__)
9
+
10
+ class Action(ABC):
11
+ """
12
+ Base class for state actions (when state has 'action:' key).
13
+ """
14
+
15
+ @abstractmethod
16
+ async def execute(
17
+ self,
18
+ action_name: str,
19
+ context: Dict[str, Any],
20
+ config: Dict[str, Any]
21
+ ) -> Dict[str, Any]:
22
+ """
23
+ Execute the action.
24
+ Returns modified context.
25
+ """
26
+ pass
27
+
28
+ class HookAction(Action):
29
+ """
30
+ Default action: delegates to machine hooks (on_action).
31
+ """
32
+
33
+ def __init__(self, hooks):
34
+ self.hooks = hooks
35
+
36
+ async def execute(
37
+ self,
38
+ action_name: str,
39
+ context: Dict[str, Any],
40
+ config: Dict[str, Any]
41
+ ) -> Dict[str, Any]:
42
+ return self.hooks.on_action(action_name, context)
43
+
44
+ # -------------------------------------------------------------------------
45
+ # Machine Invokers (Graph Execution)
46
+ # -------------------------------------------------------------------------
47
+
48
+ class MachineInvoker(ABC):
49
+ """
50
+ Interface for invoking other machines (graph execution).
51
+
52
+ See flatagents-runtime.d.ts for canonical interface definition.
53
+ """
54
+
55
+ @abstractmethod
56
+ async def invoke(
57
+ self,
58
+ caller_machine: 'FlatMachine',
59
+ target_config: Dict[str, Any],
60
+ input_data: Dict[str, Any],
61
+ execution_id: Optional[str] = None
62
+ ) -> Dict[str, Any]:
63
+ """
64
+ Invoke another machine and wait for result.
65
+
66
+ Args:
67
+ caller_machine: The machine initiating the call
68
+ target_config: Config dict for the target machine
69
+ input_data: Input to pass to the target machine
70
+ execution_id: Optional predetermined ID (for resume support)
71
+
72
+ Returns:
73
+ The target machine's output
74
+ """
75
+ pass
76
+
77
+ @abstractmethod
78
+ async def launch(
79
+ self,
80
+ caller_machine: 'FlatMachine',
81
+ target_config: Dict[str, Any],
82
+ input_data: Dict[str, Any],
83
+ execution_id: str
84
+ ) -> None:
85
+ """
86
+ Launch a machine fire-and-forget style.
87
+
88
+ The launched machine runs independently. Results are written
89
+ to the result backend using the execution_id.
90
+
91
+ Args:
92
+ caller_machine: The machine initiating the launch
93
+ target_config: Config dict for the target machine
94
+ input_data: Input to pass to the target machine
95
+ execution_id: The predetermined execution ID for the launched machine
96
+ """
97
+ pass
98
+
99
+ class InlineInvoker(MachineInvoker):
100
+ """
101
+ Default Invoker for local execution.
102
+
103
+ - invoke(): Runs target machine in same event loop, awaits result
104
+ - launch(): Creates background task, returns immediately
105
+
106
+ Both share the same persistence/lock backends as the caller.
107
+ """
108
+
109
+ def __init__(self):
110
+ # Track background tasks for cleanup
111
+ self._background_tasks: set = set()
112
+
113
+ async def invoke(
114
+ self,
115
+ caller_machine: 'FlatMachine',
116
+ target_config: Dict[str, Any],
117
+ input_data: Dict[str, Any],
118
+ execution_id: Optional[str] = None
119
+ ) -> Dict[str, Any]:
120
+ from .flatmachine import FlatMachine # lazy import to avoid cycle
121
+ import hashlib
122
+
123
+ target_name = target_config.get('data', {}).get('name', 'unknown')
124
+
125
+ # Generate execution_id if not provided
126
+ if not execution_id:
127
+ context_hash = hashlib.md5(str(sorted(input_data.items())).encode()).hexdigest()[:8]
128
+ execution_id = f"{caller_machine.execution_id}:peer:{target_name}:{context_hash}"
129
+
130
+ logger.info(f"Invoking peer machine: {target_name} (ID: {execution_id})")
131
+
132
+ target = FlatMachine(
133
+ config_dict=target_config,
134
+ persistence=caller_machine.persistence,
135
+ lock=caller_machine.lock,
136
+ result_backend=caller_machine.result_backend,
137
+ _config_dir=caller_machine._config_dir,
138
+ _execution_id=execution_id,
139
+ _parent_execution_id=caller_machine.execution_id,
140
+ )
141
+
142
+ result = await target.execute(input=input_data, resume_from=execution_id)
143
+
144
+ # Aggregate stats back to caller
145
+ caller_machine.total_api_calls += target.total_api_calls
146
+ caller_machine.total_cost += target.total_cost
147
+
148
+ return result
149
+
150
+ async def launch(
151
+ self,
152
+ caller_machine: 'FlatMachine',
153
+ target_config: Dict[str, Any],
154
+ input_data: Dict[str, Any],
155
+ execution_id: str
156
+ ) -> None:
157
+ import asyncio
158
+ from .flatmachine import FlatMachine
159
+ from .backends import make_uri
160
+
161
+ target_name = target_config.get('data', {}).get('name', 'unknown')
162
+ logger.info(f"Launching peer machine (fire-and-forget): {target_name} (ID: {execution_id})")
163
+
164
+ async def _execute_and_write():
165
+ target = FlatMachine(
166
+ config_dict=target_config,
167
+ persistence=caller_machine.persistence,
168
+ lock=caller_machine.lock,
169
+ result_backend=caller_machine.result_backend,
170
+ _config_dir=caller_machine._config_dir,
171
+ _execution_id=execution_id,
172
+ _parent_execution_id=caller_machine.execution_id,
173
+ )
174
+
175
+ try:
176
+ result = await target.execute(input=input_data)
177
+ # Write result to backend so parent can read if needed
178
+ uri = make_uri(execution_id, "result")
179
+ await caller_machine.result_backend.write(uri, result)
180
+ except Exception as e:
181
+ uri = make_uri(execution_id, "result")
182
+ await caller_machine.result_backend.write(uri, {
183
+ "_error": str(e),
184
+ "_error_type": type(e).__name__
185
+ })
186
+ raise
187
+
188
+ # Create background task
189
+ task = asyncio.create_task(_execute_and_write())
190
+ caller_machine._background_tasks.add(task)
191
+ task.add_done_callback(caller_machine._background_tasks.discard)
192
+
193
+
194
+ class QueueInvoker(MachineInvoker):
195
+ """
196
+ Invoker that enqueues launches to an external queue.
197
+
198
+ For production deployments using SQS, Cloud Tasks, etc.
199
+ Subclass and implement _enqueue() for your queue provider.
200
+ """
201
+
202
+ async def invoke(
203
+ self,
204
+ caller_machine: 'FlatMachine',
205
+ target_config: Dict[str, Any],
206
+ input_data: Dict[str, Any],
207
+ execution_id: Optional[str] = None
208
+ ) -> Dict[str, Any]:
209
+ # For queue-based invocation, we launch and then poll for result
210
+ import uuid
211
+ from .backends import make_uri
212
+
213
+ if not execution_id:
214
+ execution_id = str(uuid.uuid4())
215
+
216
+ await self.launch(caller_machine, target_config, input_data, execution_id)
217
+
218
+ # Block until result is available
219
+ uri = make_uri(execution_id, "result")
220
+ return await caller_machine.result_backend.read(uri, block=True)
221
+
222
+ async def launch(
223
+ self,
224
+ caller_machine: 'FlatMachine',
225
+ target_config: Dict[str, Any],
226
+ input_data: Dict[str, Any],
227
+ execution_id: str
228
+ ) -> None:
229
+ await self._enqueue(execution_id, target_config, input_data)
230
+
231
+ async def _enqueue(
232
+ self,
233
+ execution_id: str,
234
+ config: Dict[str, Any],
235
+ input_data: Dict[str, Any]
236
+ ) -> None:
237
+ """Override in subclass to enqueue to your queue provider."""
238
+ raise NotImplementedError("Subclass must implement _enqueue()")
239
+
File without changes
@@ -0,0 +1,189 @@
1
+ /**
2
+ * FlatAgents Configuration Schema
3
+ * ===============================
4
+ *
5
+ * An agent is a single LLM call: model + prompts + output schema.
6
+ * Workflows handle composition, branching, and loops.
7
+ *
8
+ * STRUCTURE:
9
+ * ----------
10
+ * spec - Fixed string "flatagents"
11
+ * spec_version - Semver string
12
+ * data - The agent configuration
13
+ * metadata - Extensibility layer (runners ignore unrecognized keys)
14
+ *
15
+ * DERIVED SCHEMAS:
16
+ * ----------------
17
+ * This file (/flatagent.d.ts) is the SOURCE OF TRUTH for all FlatAgent schemas.
18
+ * Other schemas (JSON Schema, etc.) are DERIVED from this file using scripts.
19
+ * See: /scripts/generate-spec-assets.ts
20
+ *
21
+ * DATA FIELDS:
22
+ * ------------
23
+ * name - Agent identifier (inferred from filename if omitted)
24
+ * model - LLM configuration
25
+ * system - System prompt (Jinja2 template)
26
+ * user - User prompt template (Jinja2)
27
+ * instruction_suffix - Optional instruction appended after user prompt
28
+ * output - Output schema (what fields we want)
29
+ * mcp - Optional MCP (Model Context Protocol) configuration
30
+ *
31
+ * MCP FIELDS:
32
+ * -----------
33
+ * servers - Map of server name to MCPServerDef
34
+ * tool_filter - Optional allow/deny lists using "server:tool" format
35
+ * tool_prompt - Jinja2 template for tool prompt (uses {{ tools }} variable)
36
+ *
37
+ * MODEL FIELDS:
38
+ * -------------
39
+ * name - Model name (e.g., "gpt-4", "zai-glm-4.6")
40
+ * provider - Provider name (e.g., "openai", "anthropic", "cerebras")
41
+ * temperature - Sampling temperature (0.0 to 2.0)
42
+ * max_tokens - Maximum tokens to generate
43
+ * top_p - Nucleus sampling parameter
44
+ * frequency_penalty - Frequency penalty (-2.0 to 2.0)
45
+ * presence_penalty - Presence penalty (-2.0 to 2.0)
46
+ *
47
+ * OUTPUT FIELD DEFINITION:
48
+ * ------------------------
49
+ * type - Field type: str, int, float, bool, json, list, object
50
+ * description - Description (used for structured output / tool calls)
51
+ * enum - Allowed values (for enum-like fields)
52
+ * required - Whether the field is required (default: true)
53
+ * items - For list type: the type of items
54
+ * properties - For object type: nested properties
55
+ *
56
+ * TEMPLATE SYNTAX:
57
+ * ----------------
58
+ * Prompts use Jinja2 templating. Available variables:
59
+ * - input.* - Values passed to the agent at runtime
60
+ *
61
+ * Example: "Question: {{ input.question }}"
62
+ *
63
+ * EXAMPLE CONFIGURATION:
64
+ * ----------------------
65
+ *
66
+ * spec: flatagents
67
+ * spec_version: "0.0.0"
68
+ *
69
+ * data:
70
+ * name: critic
71
+ *
72
+ * model:
73
+ * provider: cerebras
74
+ * name: zai-glm-4.6
75
+ * temperature: 0.5
76
+ *
77
+ * system: |
78
+ * Act as a ruthless critic. Analyze drafts for errors.
79
+ * Rate severity as: High, Medium, or Low.
80
+ *
81
+ * user: |
82
+ * Question: {{ input.question }}
83
+ * Draft: {{ input.draft }}
84
+ *
85
+ * output:
86
+ * critique:
87
+ * type: str
88
+ * description: "Specific errors found in the draft"
89
+ * severity:
90
+ * type: str
91
+ * description: "Error severity"
92
+ * enum: ["High", "Medium", "Low"]
93
+ *
94
+ * metadata:
95
+ * description: "Critiques draft answers"
96
+ * tags: ["reflection", "qa"]
97
+ *
98
+ * MCPCONFIG:
99
+ * ----------
100
+ * MCP (Model Context Protocol) configuration.
101
+ * Defines MCP servers and tool filtering rules.
102
+ * servers - MCP server definitions, keyed by server name
103
+ * tool_filter - Optional tool filtering rules
104
+ * tool_prompt - Jinja2 template for tool prompt injection.
105
+ * Available variables: tools (list of discovered tools)
106
+ * Example: "{% for tool in tools %}{{ tool.name }}: {{ tool.description }}{% endfor %}"
107
+ *
108
+ * MCPSERVERDEF:
109
+ * -------------
110
+ * MCP server definition.
111
+ * Supports stdio transport (command) or HTTP transport (server_url).
112
+ * Stdio transport:
113
+ * command - Command to start the MCP server (e.g., "npx", "python")
114
+ * args - Arguments for the command
115
+ * env - Environment variables for the server process
116
+ * HTTP transport:
117
+ * server_url - Base URL of the MCP server (e.g., "http://localhost:8000")
118
+ * headers - HTTP headers (e.g., for authentication)
119
+ * timeout - Request timeout in seconds
120
+ *
121
+ * TOOLFILTER:
122
+ * -----------
123
+ * Tool filtering rules using "server:tool" format.
124
+ * Supports wildcards: "server:*" matches all tools from a server.
125
+ * allow - Tools to allow (if specified, only these are included)
126
+ * deny - Tools to deny (takes precedence over allow)
127
+ */
128
+
129
+ export const SPEC_VERSION = "0.6.0";
130
+
131
+ export interface AgentWrapper {
132
+ spec: "flatagent";
133
+ spec_version: string;
134
+ data: AgentData;
135
+ metadata?: Record<string, any>;
136
+ }
137
+
138
+ export interface AgentData {
139
+ name?: string;
140
+ model: ModelConfig;
141
+ system: string;
142
+ user: string;
143
+ instruction_suffix?: string;
144
+ output?: OutputSchema;
145
+ mcp?: MCPConfig;
146
+ }
147
+
148
+ export interface MCPConfig {
149
+ servers: Record<string, MCPServerDef>;
150
+ tool_filter?: ToolFilter;
151
+ tool_prompt: string;
152
+ }
153
+
154
+ export interface MCPServerDef {
155
+ command?: string;
156
+ args?: string[];
157
+ env?: Record<string, string>;
158
+ server_url?: string;
159
+ headers?: Record<string, string>;
160
+ timeout?: number;
161
+ }
162
+
163
+ export interface ToolFilter {
164
+ allow?: string[];
165
+ deny?: string[];
166
+ }
167
+
168
+ export interface ModelConfig {
169
+ name: string;
170
+ provider?: string;
171
+ temperature?: number;
172
+ max_tokens?: number;
173
+ top_p?: number;
174
+ frequency_penalty?: number;
175
+ presence_penalty?: number;
176
+ }
177
+
178
+ export type OutputSchema = Record<string, OutputFieldDef>;
179
+
180
+ export interface OutputFieldDef {
181
+ type: "str" | "int" | "float" | "bool" | "json" | "list" | "object";
182
+ description?: string;
183
+ enum?: string[];
184
+ required?: boolean;
185
+ items?: OutputFieldDef;
186
+ properties?: OutputSchema;
187
+ }
188
+
189
+ export type FlatagentsConfig = AgentWrapper;