contextagent 0.1.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.
Files changed (66) hide show
  1. agentz/agent/base.py +262 -0
  2. agentz/artifacts/__init__.py +5 -0
  3. agentz/artifacts/artifact_writer.py +538 -0
  4. agentz/artifacts/reporter.py +235 -0
  5. agentz/artifacts/terminal_writer.py +100 -0
  6. agentz/context/__init__.py +6 -0
  7. agentz/context/context.py +91 -0
  8. agentz/context/conversation.py +205 -0
  9. agentz/context/data_store.py +208 -0
  10. agentz/llm/llm_setup.py +156 -0
  11. agentz/mcp/manager.py +142 -0
  12. agentz/mcp/patches.py +88 -0
  13. agentz/mcp/servers/chrome_devtools/server.py +14 -0
  14. agentz/profiles/base.py +108 -0
  15. agentz/profiles/data/data_analysis.py +38 -0
  16. agentz/profiles/data/data_loader.py +35 -0
  17. agentz/profiles/data/evaluation.py +43 -0
  18. agentz/profiles/data/model_training.py +47 -0
  19. agentz/profiles/data/preprocessing.py +47 -0
  20. agentz/profiles/data/visualization.py +47 -0
  21. agentz/profiles/manager/evaluate.py +51 -0
  22. agentz/profiles/manager/memory.py +62 -0
  23. agentz/profiles/manager/observe.py +48 -0
  24. agentz/profiles/manager/routing.py +66 -0
  25. agentz/profiles/manager/writer.py +51 -0
  26. agentz/profiles/mcp/browser.py +21 -0
  27. agentz/profiles/mcp/chrome.py +21 -0
  28. agentz/profiles/mcp/notion.py +21 -0
  29. agentz/runner/__init__.py +74 -0
  30. agentz/runner/base.py +28 -0
  31. agentz/runner/executor.py +320 -0
  32. agentz/runner/hooks.py +110 -0
  33. agentz/runner/iteration.py +142 -0
  34. agentz/runner/patterns.py +215 -0
  35. agentz/runner/tracker.py +188 -0
  36. agentz/runner/utils.py +45 -0
  37. agentz/runner/workflow.py +250 -0
  38. agentz/tools/__init__.py +20 -0
  39. agentz/tools/data_tools/__init__.py +17 -0
  40. agentz/tools/data_tools/data_analysis.py +152 -0
  41. agentz/tools/data_tools/data_loading.py +92 -0
  42. agentz/tools/data_tools/evaluation.py +175 -0
  43. agentz/tools/data_tools/helpers.py +120 -0
  44. agentz/tools/data_tools/model_training.py +192 -0
  45. agentz/tools/data_tools/preprocessing.py +229 -0
  46. agentz/tools/data_tools/visualization.py +281 -0
  47. agentz/utils/__init__.py +69 -0
  48. agentz/utils/config.py +708 -0
  49. agentz/utils/helpers.py +10 -0
  50. agentz/utils/parsers.py +142 -0
  51. agentz/utils/printer.py +539 -0
  52. contextagent-0.1.0.dist-info/METADATA +269 -0
  53. contextagent-0.1.0.dist-info/RECORD +66 -0
  54. contextagent-0.1.0.dist-info/WHEEL +5 -0
  55. contextagent-0.1.0.dist-info/licenses/LICENSE +21 -0
  56. contextagent-0.1.0.dist-info/top_level.txt +2 -0
  57. pipelines/base.py +972 -0
  58. pipelines/data_scientist.py +97 -0
  59. pipelines/data_scientist_memory.py +151 -0
  60. pipelines/experience_learner.py +0 -0
  61. pipelines/prompt_generator.py +0 -0
  62. pipelines/simple.py +78 -0
  63. pipelines/simple_browser.py +145 -0
  64. pipelines/simple_chrome.py +75 -0
  65. pipelines/simple_notion.py +103 -0
  66. pipelines/tool_builder.py +0 -0
agentz/agent/base.py ADDED
@@ -0,0 +1,262 @@
1
+ from __future__ import annotations
2
+
3
+ import json
4
+ from typing import Any, Callable, Optional
5
+
6
+ from pydantic import BaseModel
7
+
8
+ from agents import Agent, RunResult
9
+ from agents.run_context import TContext
10
+
11
+ PromptBuilder = Callable[[Any, Any, "ContextAgent"], str]
12
+
13
+
14
+ class ContextAgent(Agent[TContext]):
15
+ """Capability-centric wrapper that binds LLM + tools + typed IO contract."""
16
+
17
+ def __init__(
18
+ self,
19
+ *args: Any,
20
+ input_model: type[BaseModel] | None = None,
21
+ output_model: type[BaseModel] | None = None,
22
+ prompt_builder: PromptBuilder | None = None,
23
+ default_span_type: str = "agent",
24
+ output_parser: Optional[Callable[[str], Any]] = None,
25
+ **kwargs: Any,
26
+ ) -> None:
27
+ if output_model and kwargs.get("output_type"):
28
+ raise ValueError("Use either output_model or output_type, not both.")
29
+ if output_model is not None:
30
+ kwargs["output_type"] = output_model
31
+
32
+ super().__init__(*args, **kwargs)
33
+
34
+ self.input_model = input_model
35
+ self.output_model = self._coerce_output_model(output_model or getattr(self, "output_type", None))
36
+ self.prompt_builder = prompt_builder
37
+ self.default_span_type = default_span_type
38
+ self.output_parser = output_parser
39
+ self._pipeline = None # Optional pipeline reference for context-aware execution
40
+ self._role = None # Optional role identifier for automatic iteration tracking
41
+
42
+ @classmethod
43
+ def from_profile(cls, pipeline: Any, role: str, llm: str) -> "ContextAgent":
44
+ """Create a ContextAgent from a pipeline context and role.
45
+
46
+ Automatically looks up the profile from pipeline.context.profiles[role],
47
+ derives agent name, and binds the agent to the pipeline with the role.
48
+
49
+ Args:
50
+ pipeline: Pipeline instance (must have context.profiles attribute)
51
+ role: Role name that maps to a profile key (e.g., "observe", "evaluate")
52
+ llm: LLM model name (e.g., "gpt-4", "claude-3-5-sonnet")
53
+
54
+ Returns:
55
+ ContextAgent instance configured from the profile and bound to pipeline
56
+
57
+ Example:
58
+ agent = ContextAgent.from_profile(self, "observe", "gpt-4")
59
+ # Looks up profiles["observe"], creates agent, and binds it to pipeline
60
+ """
61
+ # Look up profile from pipeline's context
62
+ profile = pipeline.context.profiles[role]
63
+
64
+ # Auto-derive name from role
65
+ agent_name = role + "_agent" if role != "agent" else "agent"
66
+
67
+ # Get tools, default to empty list if None
68
+ # Note: Tools in profiles are currently string names, not resolved objects
69
+ # This causes errors in the agents library which expects tool objects
70
+ # For now, pass empty list to avoid runtime errors
71
+ # TODO: Implement tool resolution to make tools actually work
72
+ tools = []
73
+
74
+ agent = cls(
75
+ name=agent_name,
76
+ instructions=profile.instructions,
77
+ output_model=getattr(profile, "output_schema", None),
78
+ input_model=getattr(profile, "input_schema", None),
79
+ tools=tools,
80
+ model=llm,
81
+ )
82
+
83
+ # Bind agent to pipeline with role
84
+ agent._pipeline = pipeline
85
+ agent._role = role
86
+
87
+ return agent
88
+
89
+ @staticmethod
90
+ def _coerce_output_model(candidate: Any) -> type[BaseModel] | None:
91
+ if isinstance(candidate, type) and issubclass(candidate, BaseModel):
92
+ return candidate
93
+ return None
94
+
95
+ def _coerce_input(self, payload: Any, strict: bool = True) -> Any:
96
+ if self.input_model is None or payload is None:
97
+ return payload
98
+ if isinstance(payload, self.input_model):
99
+ return payload
100
+ if isinstance(payload, BaseModel):
101
+ return self.input_model.model_validate(payload.model_dump())
102
+ if isinstance(payload, dict):
103
+ return self.input_model.model_validate(payload)
104
+
105
+ # If strict mode is disabled, allow passthrough
106
+ if not strict:
107
+ return payload
108
+
109
+ msg = f"{self.name} expects input compatible with {self.input_model.__name__}"
110
+ raise TypeError(msg)
111
+
112
+ @staticmethod
113
+ def _to_prompt_payload(payload: Any) -> dict[str, Any]:
114
+ if payload is None:
115
+ return {}
116
+ if isinstance(payload, BaseModel):
117
+ return payload.model_dump()
118
+ if isinstance(payload, dict):
119
+ return payload
120
+ return {"input": payload}
121
+
122
+ def build_prompt(
123
+ self,
124
+ payload: Any = None,
125
+ *,
126
+ context: Any = None,
127
+ template: Optional[str] = None,
128
+ ) -> str:
129
+ validated = self._coerce_input(payload)
130
+
131
+ if self.prompt_builder:
132
+ return self.prompt_builder(validated, context, self)
133
+
134
+ if context is not None and template:
135
+ builder = getattr(context, "build_prompt", None)
136
+ if builder is None:
137
+ raise AttributeError("Context object must expose build_prompt(...)")
138
+ prompt_data = self._to_prompt_payload(validated)
139
+ return builder(agent=self, template_name=template, data=prompt_data)
140
+
141
+ if isinstance(validated, str):
142
+ return validated
143
+ if isinstance(validated, BaseModel):
144
+ return validated.model_dump_json(indent=2)
145
+ if isinstance(validated, dict):
146
+ return json.dumps(validated, indent=2)
147
+
148
+ if validated is None and isinstance(self.instructions, str):
149
+ return self.instructions
150
+
151
+ return str(validated)
152
+
153
+ async def invoke(
154
+ self,
155
+ *,
156
+ pipeline: Any,
157
+ span_name: str,
158
+ payload: Any = None,
159
+ prompt: Optional[str] = None,
160
+ context: Any = None,
161
+ template: Optional[str] = None,
162
+ span_type: Optional[str] = None,
163
+ output_model: Optional[type[BaseModel]] = None,
164
+ printer_key: Optional[str] = None,
165
+ printer_title: Optional[str] = None,
166
+ printer_group_id: Optional[str] = None,
167
+ printer_border_style: Optional[str] = None,
168
+ **span_kwargs: Any,
169
+ ) -> Any:
170
+ instructions = prompt or self.build_prompt(payload, context=context, template=template)
171
+ model = output_model or self.output_model
172
+
173
+ return await pipeline.agent_step(
174
+ agent=self,
175
+ instructions=instructions,
176
+ span_name=span_name,
177
+ span_type=span_type or self.default_span_type,
178
+ output_model=model,
179
+ printer_key=printer_key,
180
+ printer_title=printer_title,
181
+ printer_group_id=printer_group_id,
182
+ printer_border_style=printer_border_style,
183
+ **span_kwargs,
184
+ )
185
+
186
+ async def __call__(self, payload: Any = None, group_id: Optional[str] = None) -> Any:
187
+ """Make ContextAgent callable directly.
188
+
189
+ This allows usage like: result = await agent(input_data)
190
+
191
+ When called within a pipeline context (self._pipeline is set), uses the
192
+ pipeline's agent_step for full tracking/tracing. Otherwise, uses ContextRunner.
193
+
194
+ Note: When calling directly without pipeline context, input validation
195
+ is relaxed to allow string inputs even if agent has a defined input_model.
196
+
197
+ Args:
198
+ payload: Input data for the agent
199
+ group_id: Optional group ID for tracking (used with pipeline context)
200
+
201
+ Returns:
202
+ Parsed output if in pipeline context, otherwise RunResult
203
+ """
204
+ # Build prompt with non-strict input coercion
205
+ # This allows string inputs to pass through without validation errors
206
+ validated = self._coerce_input(payload, strict=False)
207
+
208
+ if isinstance(validated, str):
209
+ instructions = validated
210
+ elif isinstance(validated, BaseModel):
211
+ instructions = validated.model_dump_json(indent=2)
212
+ elif isinstance(validated, dict):
213
+ import json
214
+ instructions = json.dumps(validated, indent=2)
215
+ elif validated is None and isinstance(self.instructions, str):
216
+ instructions = self.instructions
217
+ else:
218
+ instructions = str(validated)
219
+
220
+ # If pipeline context is available, use it for full tracking
221
+ if self._pipeline is not None:
222
+ result = await self._pipeline.agent_step(
223
+ agent=self,
224
+ instructions=instructions,
225
+ group_id=group_id,
226
+ )
227
+ # Extract final output for cleaner API
228
+ output = result.final_output if hasattr(result, 'final_output') else result
229
+
230
+ # Automatic iteration tracking based on role
231
+ if self._role and hasattr(self._pipeline, 'context'):
232
+ try:
233
+ iteration = self._pipeline.context.current_iteration
234
+
235
+ # Special handling for "observe" role - set iteration.observation
236
+ if self._role == "observe":
237
+ serialized = self._pipeline._serialize_output(output)
238
+ iteration.observation = serialized
239
+
240
+ # Record structured payload for all roles
241
+ self._pipeline._record_structured_payload(output, context_label=self._role)
242
+ except Exception:
243
+ # Silently skip if context/iteration not available
244
+ pass
245
+
246
+ return output
247
+
248
+ # Otherwise, use ContextRunner to execute the agent
249
+ from agentz.runner import ContextRunner
250
+
251
+ result = await ContextRunner.run(
252
+ starting_agent=self,
253
+ input=instructions,
254
+ )
255
+
256
+ return result
257
+
258
+ async def parse_output(self, run_result: RunResult) -> RunResult:
259
+ """Apply legacy string parser only when no structured output is configured."""
260
+ if self.output_parser and self.output_model is None:
261
+ run_result.final_output = self.output_parser(run_result.final_output)
262
+ return run_result
@@ -0,0 +1,5 @@
1
+ """Reporting utilities for pipeline runs."""
2
+
3
+ from .reporter import RunReporter
4
+
5
+ __all__ = ["RunReporter"]