coreason-manifest 0.7.0__py3-none-any.whl → 0.9.0__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.
@@ -0,0 +1,392 @@
1
+ from datetime import datetime, timezone
2
+ from typing import Any, Dict, Generic, Literal, Optional, Protocol, TypeVar, runtime_checkable
3
+ from uuid import uuid4
4
+
5
+ from pydantic import BaseModel, ConfigDict, Field
6
+
7
+ # --- CloudEvents v1.0 Implementation ---
8
+
9
+ T = TypeVar("T")
10
+
11
+
12
+ @runtime_checkable
13
+ class CloudEventSource(Protocol):
14
+ def as_cloud_event_payload(self) -> Any: ...
15
+
16
+
17
+ class CloudEvent(BaseModel, Generic[T]):
18
+ """Standard CloudEvent v1.0 Envelope."""
19
+
20
+ model_config = ConfigDict(extra="allow", populate_by_name=True)
21
+
22
+ specversion: Literal["1.0"] = "1.0"
23
+ type: str = Field(..., description="Type of occurrence (e.g. ai.coreason.node.started)")
24
+ source: str = Field(..., description="URI identifying the event producer")
25
+ id: str = Field(default_factory=lambda: str(uuid4()), description="Unique event identifier")
26
+ time: datetime = Field(
27
+ default_factory=lambda: datetime.now(timezone.utc), description="Timestamp of when the occurrence happened"
28
+ )
29
+ datacontenttype: Literal["application/json"] = "application/json"
30
+
31
+ data: Optional[T] = Field(None, description="The event payload")
32
+
33
+ # Distributed Tracing Extensions (W3C)
34
+ traceparent: Optional[str] = Field(None, description="W3C Trace Context traceparent")
35
+ tracestate: Optional[str] = Field(None, description="W3C Trace Context tracestate")
36
+
37
+
38
+ # --- OTel Semantic Conventions ---
39
+
40
+
41
+ class GenAIUsage(BaseModel):
42
+ """GenAI Usage metrics."""
43
+
44
+ input_tokens: Optional[int] = Field(None, alias="input_tokens")
45
+ output_tokens: Optional[int] = Field(None, alias="output_tokens")
46
+
47
+ model_config = ConfigDict(populate_by_name=True)
48
+
49
+
50
+ class GenAIRequest(BaseModel):
51
+ """GenAI Request details."""
52
+
53
+ model: Optional[str] = None
54
+ temperature: Optional[float] = None
55
+ top_p: Optional[float] = None
56
+
57
+
58
+ class GenAICompletion(BaseModel):
59
+ """GenAI Completion details."""
60
+
61
+ chunk: Optional[str] = None
62
+ finish_reason: Optional[str] = None
63
+
64
+
65
+ class GenAISemantics(BaseModel):
66
+ """OpenTelemetry GenAI Semantic Conventions."""
67
+
68
+ system: Optional[str] = None
69
+ usage: Optional[GenAIUsage] = None
70
+ request: Optional[GenAIRequest] = None
71
+ completion: Optional[GenAICompletion] = None
72
+
73
+
74
+ # --- Graph Event Wrapper ---
75
+
76
+
77
+ class GraphEvent(BaseModel):
78
+ """The atomic unit of communication between the Engine (MACO) and the UI (Flutter).
79
+
80
+ Standardized IDs:
81
+ - run_id: Workflow execution ID.
82
+ - trace_id: OpenTelemetry Distributed Trace ID.
83
+
84
+ Attributes:
85
+ event_type: The type of the event.
86
+ run_id: The unique ID of the workflow run.
87
+ trace_id: The trace ID (defaults to "unknown").
88
+ node_id: The ID of the node associated with the event.
89
+ timestamp: The timestamp of the event.
90
+ sequence_id: Optional sequence ID.
91
+ payload: The event payload containing logic output.
92
+ visual_metadata: Metadata for UI visualization.
93
+ """
94
+
95
+ model_config = ConfigDict(extra="forbid")
96
+
97
+ event_type: Literal[
98
+ "NODE_INIT",
99
+ "NODE_START",
100
+ "NODE_STREAM",
101
+ "NODE_DONE",
102
+ "NODE_SKIPPED",
103
+ "EDGE_ACTIVE",
104
+ "COUNCIL_VOTE",
105
+ "ERROR",
106
+ "NODE_RESTORED",
107
+ "ARTIFACT_GENERATED",
108
+ ]
109
+ run_id: str
110
+ trace_id: str = Field(
111
+ default_factory=lambda: "unknown"
112
+ ) # Default for compatibility if missing in some legacy calls
113
+ node_id: str # Required per BRD and existing tests
114
+ timestamp: float
115
+ sequence_id: Optional[int] = None # Optional for internal use
116
+
117
+ # The payload contains the actual reasoning/data
118
+ payload: Dict[str, Any] = Field(..., description="The logic output")
119
+
120
+ # Visual Metadata drives the Flutter animation engine
121
+ visual_metadata: Dict[str, str] = Field(
122
+ ..., description="Hints for UI: color='#00FF00', animation='pulse', progress='0.5'"
123
+ )
124
+
125
+
126
+ # --- Base Models ---
127
+
128
+
129
+ class BaseNodePayload(BaseModel):
130
+ """Base model for node-related events."""
131
+
132
+ model_config = ConfigDict(extra="ignore")
133
+ node_id: str
134
+
135
+ # Common OTel attributes that might appear in payload
136
+ model: Optional[str] = None
137
+ system: Optional[str] = None
138
+
139
+ def as_cloud_event_payload(self) -> Any:
140
+ return self
141
+
142
+
143
+ # --- Payload Models ---
144
+
145
+
146
+ class NodeInit(BaseNodePayload):
147
+ """Payload for NODE_INIT event."""
148
+
149
+ type: str = "DEFAULT"
150
+ visual_cue: str = "IDLE"
151
+
152
+
153
+ class NodeStarted(BaseNodePayload):
154
+ """Payload for NODE_START event."""
155
+
156
+ timestamp: float
157
+ status: Literal["RUNNING"] = "RUNNING"
158
+ visual_cue: str = "PULSE"
159
+ input_tokens: Optional[int] = None
160
+
161
+ def as_cloud_event_payload(self) -> Any:
162
+ semantics = GenAISemantics()
163
+ has_semantics = False
164
+
165
+ if self.input_tokens is not None:
166
+ semantics.usage = GenAIUsage(input_tokens=self.input_tokens)
167
+ has_semantics = True
168
+
169
+ if self.model:
170
+ semantics.request = GenAIRequest(model=self.model)
171
+ has_semantics = True
172
+
173
+ if self.system:
174
+ semantics.system = self.system
175
+ has_semantics = True
176
+
177
+ return StandardizedNodeStarted(
178
+ node_id=self.node_id,
179
+ status=self.status,
180
+ gen_ai=semantics if has_semantics else None,
181
+ )
182
+
183
+
184
+ class NodeCompleted(BaseNodePayload):
185
+ """Payload for NODE_DONE event."""
186
+
187
+ output_summary: str
188
+ status: Literal["SUCCESS"] = "SUCCESS"
189
+ visual_cue: str = "GREEN_GLOW"
190
+ cost: Optional[float] = None
191
+
192
+ def as_cloud_event_payload(self) -> Any:
193
+ semantics = GenAISemantics()
194
+ has_semantics = False
195
+
196
+ if self.model:
197
+ semantics.request = GenAIRequest(model=self.model)
198
+ has_semantics = True
199
+
200
+ return StandardizedNodeCompleted(
201
+ node_id=self.node_id,
202
+ output_summary=self.output_summary,
203
+ status=self.status,
204
+ gen_ai=semantics if has_semantics else None,
205
+ )
206
+
207
+
208
+ class NodeRestored(BaseNodePayload):
209
+ """Payload for NODE_RESTORED event."""
210
+
211
+ output_summary: str
212
+ status: Literal["RESTORED"] = "RESTORED"
213
+ visual_cue: str = "INSTANT_GREEN"
214
+
215
+
216
+ class NodeSkipped(BaseNodePayload):
217
+ """Payload for NODE_SKIPPED event."""
218
+
219
+ status: Literal["SKIPPED"] = "SKIPPED"
220
+ visual_cue: str = "GREY_OUT"
221
+
222
+
223
+ class NodeStream(BaseNodePayload):
224
+ """Payload for NODE_STREAM event."""
225
+
226
+ chunk: str
227
+ visual_cue: str = "TEXT_BUBBLE"
228
+
229
+ def as_cloud_event_payload(self) -> Any:
230
+ semantics = GenAISemantics(completion=GenAICompletion(chunk=self.chunk))
231
+ if self.model:
232
+ if not semantics.request:
233
+ semantics.request = GenAIRequest()
234
+ semantics.request.model = self.model
235
+
236
+ return StandardizedNodeStream(node_id=self.node_id, gen_ai=semantics)
237
+
238
+
239
+ class ArtifactGenerated(BaseNodePayload):
240
+ """Payload for ARTIFACT_GENERATED event."""
241
+
242
+ artifact_type: str = "PDF"
243
+ url: str
244
+
245
+
246
+ class EdgeTraversed(BaseModel):
247
+ """Payload for EDGE_ACTIVE event."""
248
+
249
+ model_config = ConfigDict(extra="ignore")
250
+ source: str
251
+ target: str
252
+ animation_speed: str = "FAST"
253
+
254
+ def as_cloud_event_payload(self) -> Any:
255
+ return self
256
+
257
+
258
+ class CouncilVote(BaseNodePayload):
259
+ """Payload for COUNCIL_VOTE event."""
260
+
261
+ votes: Dict[str, str]
262
+
263
+
264
+ class WorkflowError(BaseNodePayload):
265
+ """Payload for ERROR event."""
266
+
267
+ error_message: str
268
+ stack_trace: str
269
+ input_snapshot: Dict[str, Any]
270
+ status: Literal["ERROR"] = "ERROR"
271
+ visual_cue: str = "RED_FLASH"
272
+
273
+
274
+ # --- Standardized Payloads ---
275
+
276
+
277
+ class StandardizedNodeStarted(BaseNodePayload):
278
+ """Standardized payload for node start with OTel support."""
279
+
280
+ gen_ai: Optional[GenAISemantics] = None
281
+ status: Literal["RUNNING"] = "RUNNING"
282
+
283
+
284
+ class StandardizedNodeCompleted(BaseNodePayload):
285
+ """Standardized payload for node completion with OTel support."""
286
+
287
+ gen_ai: Optional[GenAISemantics] = None
288
+ output_summary: str
289
+ status: Literal["SUCCESS"] = "SUCCESS"
290
+
291
+
292
+ class StandardizedNodeStream(BaseNodePayload):
293
+ """Standardized payload for node stream with OTel support."""
294
+
295
+ gen_ai: Optional[GenAISemantics] = None
296
+
297
+
298
+ # --- Aliases for Backward Compatibility ---
299
+ # These ensure that code importing `NodeInitPayload` still works.
300
+ NodeInitPayload = NodeInit
301
+ NodeStartedPayload = NodeStarted
302
+ NodeCompletedPayload = NodeCompleted
303
+ NodeSkippedPayload = NodeSkipped
304
+ NodeStreamPayload = NodeStream
305
+ EdgeTraversedPayload = EdgeTraversed
306
+ ArtifactGeneratedPayload = ArtifactGenerated
307
+ CouncilVotePayload = CouncilVote
308
+ WorkflowErrorPayload = WorkflowError
309
+
310
+ # --- Migration Logic ---
311
+
312
+
313
+ def migrate_graph_event_to_cloud_event(event: GraphEvent) -> CloudEvent[Any]:
314
+ """Migrates a legacy GraphEvent to a CloudEvent v1.0."""
315
+
316
+ ce_type_map = {
317
+ "NODE_START": "ai.coreason.node.started",
318
+ "NODE_STREAM": "ai.coreason.node.stream",
319
+ "NODE_DONE": "ai.coreason.node.completed",
320
+ }
321
+ ce_type = ce_type_map.get(event.event_type, f"ai.coreason.legacy.{event.event_type.lower()}")
322
+ ce_source = f"urn:node:{event.node_id}"
323
+
324
+ # Prepare payload dict with node_id injected
325
+ payload_dict = event.payload.copy()
326
+ payload_dict["node_id"] = event.node_id
327
+ # Inject timestamp if missing (required by some payloads like NodeStarted)
328
+ if "timestamp" not in payload_dict:
329
+ payload_dict["timestamp"] = event.timestamp
330
+
331
+ # Mapping event types to their corresponding payload classes
332
+ event_class_map: Dict[str, Any] = {
333
+ "NODE_INIT": NodeInit,
334
+ "NODE_START": NodeStarted,
335
+ "NODE_DONE": NodeCompleted,
336
+ "NODE_STREAM": NodeStream,
337
+ "NODE_SKIPPED": NodeSkipped,
338
+ "NODE_RESTORED": NodeRestored,
339
+ "ERROR": WorkflowError,
340
+ "ARTIFACT_GENERATED": ArtifactGenerated,
341
+ "COUNCIL_VOTE": CouncilVote,
342
+ "EDGE_ACTIVE": EdgeTraversed,
343
+ }
344
+
345
+ data: Any = None
346
+ payload_class = event_class_map.get(event.event_type)
347
+
348
+ if payload_class:
349
+ try:
350
+ # Instantiate the payload object
351
+ payload_obj = payload_class(**payload_dict)
352
+ if isinstance(payload_obj, CloudEventSource):
353
+ data = payload_obj.as_cloud_event_payload()
354
+ else:
355
+ data = payload_obj # pragma: no cover
356
+ except Exception:
357
+ # Fallback if instantiation fails
358
+ data = event.payload
359
+ else:
360
+ data = event.payload
361
+
362
+ # UI Metadata as extension
363
+ extensions = {
364
+ "com_coreason_ui_cue": event.visual_metadata.get("animation") or event.payload.get("visual_cue"),
365
+ "com_coreason_ui_metadata": event.visual_metadata,
366
+ }
367
+
368
+ # Filter out None values in extensions
369
+ # For dictionaries, we want to filter out empty dicts too.
370
+ filtered_extensions = {}
371
+ for k, v in extensions.items():
372
+ if v is None:
373
+ continue
374
+ if isinstance(v, dict) and not v:
375
+ continue
376
+ if isinstance(v, str) and not v:
377
+ continue
378
+ # Also check if it's a dict containing only empty strings (recursive check not needed for now, just 1 level)
379
+ if isinstance(v, dict) and all(isinstance(val, str) and not val for val in v.values()):
380
+ continue
381
+
382
+ filtered_extensions[k] = v
383
+
384
+ extensions = filtered_extensions
385
+
386
+ return CloudEvent(
387
+ type=ce_type,
388
+ source=ce_source,
389
+ time=datetime.fromtimestamp(event.timestamp, timezone.utc),
390
+ data=data,
391
+ **extensions,
392
+ )
@@ -0,0 +1,126 @@
1
+ from enum import Enum
2
+ from typing import Any, Dict, List, Literal, Optional, Union
3
+
4
+ from pydantic import BaseModel, ConfigDict, Field
5
+
6
+ # --- Enums ---
7
+
8
+
9
+ class Role(str, Enum):
10
+ SYSTEM = "system"
11
+ USER = "user"
12
+ ASSISTANT = "assistant"
13
+ TOOL = "tool"
14
+
15
+
16
+ class Modality(str, Enum):
17
+ TEXT = "text"
18
+ IMAGE = "image"
19
+ AUDIO = "audio"
20
+ VIDEO = "video"
21
+
22
+
23
+ # --- Message Parts ---
24
+
25
+
26
+ class TextPart(BaseModel):
27
+ """Represents text content sent to or received from the model."""
28
+
29
+ model_config = ConfigDict(extra="ignore")
30
+ type: Literal["text"] = "text"
31
+ content: str
32
+
33
+
34
+ class BlobPart(BaseModel):
35
+ """Represents blob binary data sent inline to the model."""
36
+
37
+ model_config = ConfigDict(extra="ignore")
38
+ type: Literal["blob"] = "blob"
39
+ content: str # Base64 encoded string
40
+ modality: Modality
41
+ mime_type: Optional[str] = None
42
+
43
+
44
+ class FilePart(BaseModel):
45
+ """Represents an external referenced file sent to the model by file id."""
46
+
47
+ model_config = ConfigDict(extra="ignore")
48
+ type: Literal["file"] = "file"
49
+ file_id: str
50
+ modality: Modality
51
+ mime_type: Optional[str] = None
52
+
53
+
54
+ class UriPart(BaseModel):
55
+ """Represents an external referenced file sent to the model by URI."""
56
+
57
+ model_config = ConfigDict(extra="ignore")
58
+ type: Literal["uri"] = "uri"
59
+ uri: str
60
+ modality: Modality
61
+ mime_type: Optional[str] = None
62
+
63
+
64
+ class ToolCallRequestPart(BaseModel):
65
+ """Represents a tool call requested by the model."""
66
+
67
+ model_config = ConfigDict(extra="ignore")
68
+ type: Literal["tool_call"] = "tool_call"
69
+ name: str
70
+ arguments: Dict[str, Any] # Structured arguments
71
+ id: Optional[str] = None
72
+
73
+
74
+ class ToolCallResponsePart(BaseModel):
75
+ """Represents a tool call result sent to the model."""
76
+
77
+ model_config = ConfigDict(extra="ignore")
78
+ type: Literal["tool_call_response"] = "tool_call_response"
79
+ response: Any # The result of the tool call
80
+ id: Optional[str] = None
81
+
82
+
83
+ class ReasoningPart(BaseModel):
84
+ """Represents reasoning/thinking content received from the model."""
85
+
86
+ model_config = ConfigDict(extra="ignore")
87
+ type: Literal["reasoning"] = "reasoning"
88
+ content: str
89
+
90
+
91
+ # --- Union of All Parts ---
92
+
93
+ Part = Union[TextPart, BlobPart, FilePart, UriPart, ToolCallRequestPart, ToolCallResponsePart, ReasoningPart]
94
+
95
+ # --- Main Message Model ---
96
+
97
+
98
+ class ChatMessage(BaseModel):
99
+ """Represents a message in a conversation with an LLM."""
100
+
101
+ model_config = ConfigDict(extra="ignore")
102
+
103
+ role: Role
104
+ parts: List[Part] = Field(..., description="List of message parts that make up the message content.")
105
+ name: Optional[str] = None
106
+
107
+
108
+ # --- Backward Compatibility ---
109
+
110
+
111
+ class FunctionCall(BaseModel):
112
+ """Deprecated: Use ToolCallRequestPart instead."""
113
+
114
+ name: str
115
+ arguments: str
116
+
117
+
118
+ class ToolCall(BaseModel):
119
+ """Deprecated: Use ToolCallRequestPart instead."""
120
+
121
+ id: str
122
+ type: str = "function"
123
+ function: FunctionCall
124
+
125
+
126
+ Message = ChatMessage
@@ -1,19 +1,50 @@
1
- from pydantic import BaseModel
1
+ from datetime import datetime
2
+ from enum import Enum
3
+ from typing import Any, Dict, List
4
+ from uuid import UUID
5
+
6
+ from pydantic import BaseModel, Field
7
+
8
+
9
+ class ValidationLogic(str, Enum):
10
+ """Logic used to validate the scenario outcome."""
11
+
12
+ EXACT_MATCH = "exact_match"
13
+ FUZZY = "fuzzy"
14
+ CODE_EVAL = "code_eval"
2
15
 
3
16
 
4
17
  class SimulationScenario(BaseModel):
5
18
  """Definition of a simulation scenario."""
6
19
 
7
- pass
20
+ id: str = Field(..., description="Unique identifier for the scenario.")
21
+ name: str = Field(..., description="Name of the scenario.")
22
+ objective: str = Field(..., description="The prompt/task instructions.")
23
+ difficulty: int = Field(..., description="Difficulty level (1-3, aligning with GAIA levels).", ge=1, le=3)
24
+ expected_outcome: Any = Field(..., description="The ground truth for validation.")
25
+ validation_logic: ValidationLogic = Field(..., description="Logic used to validate the outcome.")
8
26
 
9
27
 
10
- class SimulationTrace(BaseModel):
11
- """Trace of a simulation execution."""
28
+ class SimulationStep(BaseModel):
29
+ """The atomic unit of execution in a simulation."""
12
30
 
13
- pass
31
+ step_id: UUID = Field(..., description="Atomic unit of execution ID.")
32
+ timestamp: datetime = Field(..., description="Execution timestamp.")
33
+ node_id: str = Field(..., description="The graph node executed.")
34
+ inputs: Dict[str, Any] = Field(..., description="Snapshot of entry state.")
35
+ thought: str = Field(..., description="The Chain-of-Thought reasoning.")
36
+ action: Dict[str, Any] = Field(..., description="Tool calls or API requests.")
37
+ observation: Dict[str, Any] = Field(..., description="Tool outputs.")
38
+ snapshot: Dict[str, Any] = Field(
39
+ default_factory=dict, description="Full copy of the graph state at the completion of this step."
40
+ )
14
41
 
15
42
 
16
- class SimulationTurn(BaseModel):
17
- """A single turn in a simulation."""
43
+ class SimulationTrace(BaseModel):
44
+ """Trace of a simulation execution."""
18
45
 
19
- pass
46
+ trace_id: UUID = Field(..., description="Unique trace identifier.")
47
+ agent_version: str = Field(..., description="Agent SemVer version.")
48
+ steps: List[SimulationStep] = Field(..., description="List of execution steps.")
49
+ outcome: Dict[str, Any] = Field(..., description="Final result.")
50
+ metrics: Dict[str, Any] = Field(..., description="Execution metrics (e.g., token usage, cost).")