sondera-harness 0.6.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 (77) hide show
  1. sondera/__init__.py +111 -0
  2. sondera/__main__.py +4 -0
  3. sondera/adk/__init__.py +3 -0
  4. sondera/adk/analyze.py +222 -0
  5. sondera/adk/plugin.py +387 -0
  6. sondera/cli.py +22 -0
  7. sondera/exceptions.py +167 -0
  8. sondera/harness/__init__.py +6 -0
  9. sondera/harness/abc.py +102 -0
  10. sondera/harness/cedar/__init__.py +0 -0
  11. sondera/harness/cedar/harness.py +363 -0
  12. sondera/harness/cedar/schema.py +225 -0
  13. sondera/harness/sondera/__init__.py +0 -0
  14. sondera/harness/sondera/_grpc.py +354 -0
  15. sondera/harness/sondera/harness.py +890 -0
  16. sondera/langgraph/__init__.py +15 -0
  17. sondera/langgraph/analyze.py +543 -0
  18. sondera/langgraph/exceptions.py +19 -0
  19. sondera/langgraph/graph.py +210 -0
  20. sondera/langgraph/middleware.py +454 -0
  21. sondera/proto/google/protobuf/any_pb2.py +37 -0
  22. sondera/proto/google/protobuf/any_pb2.pyi +14 -0
  23. sondera/proto/google/protobuf/any_pb2_grpc.py +24 -0
  24. sondera/proto/google/protobuf/duration_pb2.py +37 -0
  25. sondera/proto/google/protobuf/duration_pb2.pyi +14 -0
  26. sondera/proto/google/protobuf/duration_pb2_grpc.py +24 -0
  27. sondera/proto/google/protobuf/empty_pb2.py +37 -0
  28. sondera/proto/google/protobuf/empty_pb2.pyi +9 -0
  29. sondera/proto/google/protobuf/empty_pb2_grpc.py +24 -0
  30. sondera/proto/google/protobuf/struct_pb2.py +47 -0
  31. sondera/proto/google/protobuf/struct_pb2.pyi +49 -0
  32. sondera/proto/google/protobuf/struct_pb2_grpc.py +24 -0
  33. sondera/proto/google/protobuf/timestamp_pb2.py +37 -0
  34. sondera/proto/google/protobuf/timestamp_pb2.pyi +14 -0
  35. sondera/proto/google/protobuf/timestamp_pb2_grpc.py +24 -0
  36. sondera/proto/google/protobuf/wrappers_pb2.py +53 -0
  37. sondera/proto/google/protobuf/wrappers_pb2.pyi +59 -0
  38. sondera/proto/google/protobuf/wrappers_pb2_grpc.py +24 -0
  39. sondera/proto/sondera/__init__.py +0 -0
  40. sondera/proto/sondera/core/__init__.py +0 -0
  41. sondera/proto/sondera/core/v1/__init__.py +0 -0
  42. sondera/proto/sondera/core/v1/primitives_pb2.py +88 -0
  43. sondera/proto/sondera/core/v1/primitives_pb2.pyi +259 -0
  44. sondera/proto/sondera/core/v1/primitives_pb2_grpc.py +24 -0
  45. sondera/proto/sondera/harness/__init__.py +0 -0
  46. sondera/proto/sondera/harness/v1/__init__.py +0 -0
  47. sondera/proto/sondera/harness/v1/harness_pb2.py +81 -0
  48. sondera/proto/sondera/harness/v1/harness_pb2.pyi +192 -0
  49. sondera/proto/sondera/harness/v1/harness_pb2_grpc.py +498 -0
  50. sondera/py.typed +0 -0
  51. sondera/settings.py +20 -0
  52. sondera/strands/__init__.py +5 -0
  53. sondera/strands/analyze.py +244 -0
  54. sondera/strands/harness.py +333 -0
  55. sondera/tui/__init__.py +0 -0
  56. sondera/tui/app.py +309 -0
  57. sondera/tui/screens/__init__.py +5 -0
  58. sondera/tui/screens/adjudication.py +184 -0
  59. sondera/tui/screens/agent.py +158 -0
  60. sondera/tui/screens/trajectory.py +158 -0
  61. sondera/tui/widgets/__init__.py +23 -0
  62. sondera/tui/widgets/agent_card.py +94 -0
  63. sondera/tui/widgets/agent_list.py +73 -0
  64. sondera/tui/widgets/recent_adjudications.py +52 -0
  65. sondera/tui/widgets/recent_trajectories.py +54 -0
  66. sondera/tui/widgets/summary.py +57 -0
  67. sondera/tui/widgets/tool_card.py +33 -0
  68. sondera/tui/widgets/violation_panel.py +72 -0
  69. sondera/tui/widgets/violations_list.py +78 -0
  70. sondera/tui/widgets/violations_summary.py +104 -0
  71. sondera/types.py +346 -0
  72. sondera_harness-0.6.0.dist-info/METADATA +323 -0
  73. sondera_harness-0.6.0.dist-info/RECORD +77 -0
  74. sondera_harness-0.6.0.dist-info/WHEEL +5 -0
  75. sondera_harness-0.6.0.dist-info/entry_points.txt +2 -0
  76. sondera_harness-0.6.0.dist-info/licenses/LICENSE +21 -0
  77. sondera_harness-0.6.0.dist-info/top_level.txt +1 -0
@@ -0,0 +1,354 @@
1
+ from datetime import UTC, datetime
2
+
3
+ from google.protobuf import struct_pb2
4
+ from google.protobuf.json_format import MessageToDict
5
+
6
+ from sondera.proto.sondera.core.v1 import primitives_pb2
7
+ from sondera.proto.sondera.harness.v1 import harness_pb2
8
+ from sondera.types import (
9
+ AdjudicatedStep,
10
+ AdjudicatedTrajectory,
11
+ Adjudication,
12
+ AdjudicationRecord,
13
+ Agent,
14
+ Content,
15
+ Decision,
16
+ Parameter,
17
+ PolicyAnnotation,
18
+ PolicyEngineMode,
19
+ PromptContent,
20
+ Role,
21
+ Stage,
22
+ Tool,
23
+ ToolRequestContent,
24
+ ToolResponseContent,
25
+ Trajectory,
26
+ TrajectoryStatus,
27
+ TrajectoryStep,
28
+ )
29
+
30
+
31
+ def _convert_sdk_stage_to_pb(stage: Stage) -> primitives_pb2.Stage:
32
+ """Convert SDK Stage to protobuf Stage."""
33
+ stage_map = {
34
+ Stage.PRE_RUN: primitives_pb2.STAGE_PRE_RUN,
35
+ Stage.PRE_MODEL: primitives_pb2.STAGE_PRE_MODEL,
36
+ Stage.POST_MODEL: primitives_pb2.STAGE_POST_MODEL,
37
+ Stage.PRE_TOOL: primitives_pb2.STAGE_PRE_TOOL,
38
+ Stage.POST_TOOL: primitives_pb2.STAGE_POST_TOOL,
39
+ Stage.POST_RUN: primitives_pb2.STAGE_POST_RUN,
40
+ }
41
+ return stage_map[stage]
42
+
43
+
44
+ def _convert_sdk_role_to_pb(role: Role) -> primitives_pb2.Role:
45
+ """Convert SDK Role to protobuf Role."""
46
+ role_map = {
47
+ Role.USER: primitives_pb2.ROLE_USER,
48
+ Role.MODEL: primitives_pb2.ROLE_MODEL,
49
+ Role.TOOL: primitives_pb2.ROLE_TOOL,
50
+ Role.SYSTEM: primitives_pb2.ROLE_SYSTEM,
51
+ }
52
+ return role_map[role]
53
+
54
+
55
+ def _convert_sdk_content_to_pb(content: Content) -> primitives_pb2.Content:
56
+ """Convert SDK Content to protobuf Content."""
57
+ if isinstance(content, PromptContent):
58
+ return primitives_pb2.Content(prompt=primitives_pb2.Prompt(text=content.text))
59
+ elif isinstance(content, ToolRequestContent):
60
+ # Convert Python dict to protobuf Value
61
+ value = struct_pb2.Value()
62
+ value.struct_value.update(content.args)
63
+ return primitives_pb2.Content(
64
+ tool_request=primitives_pb2.ToolRequest(tool_id=content.tool_id, args=value)
65
+ )
66
+ elif isinstance(content, ToolResponseContent):
67
+ # Convert Python dict to protobuf Value
68
+ value = struct_pb2.Value()
69
+ if isinstance(content.response, dict):
70
+ value.struct_value.update(content.response)
71
+ elif isinstance(content.response, list | tuple):
72
+ value.list_value.extend(content.response)
73
+ else:
74
+ value.string_value = str(content.response)
75
+ return primitives_pb2.Content(
76
+ tool_response=primitives_pb2.ToolResponse(
77
+ tool_id=content.tool_id, response=value
78
+ )
79
+ )
80
+ else:
81
+ raise ValueError(f"Unsupported content type: {type(content)}")
82
+
83
+
84
+ def _convert_pb_adjudication_to_sdk(
85
+ adjudication: primitives_pb2.Adjudication,
86
+ ) -> Adjudication:
87
+ """Convert protobuf Adjudication to SDK Adjudication."""
88
+ # Convert decision
89
+ decision_map = {
90
+ primitives_pb2.DECISION_ALLOW: Decision.ALLOW,
91
+ primitives_pb2.DECISION_DENY: Decision.DENY,
92
+ primitives_pb2.DECISION_ESCALATE: Decision.ESCALATE,
93
+ }
94
+
95
+ # Convert annotations
96
+ annotations = [
97
+ PolicyAnnotation(
98
+ id=ann.id,
99
+ description=ann.description,
100
+ custom=dict(ann.custom),
101
+ )
102
+ for ann in adjudication.annotations
103
+ ]
104
+
105
+ return Adjudication(
106
+ decision=decision_map[adjudication.decision],
107
+ reason=adjudication.reason,
108
+ annotations=annotations,
109
+ )
110
+
111
+
112
+ def _convert_pb_agent_to_sdk(pb_agent: primitives_pb2.Agent) -> Agent:
113
+ """Convert protobuf Agent to SDK Agent."""
114
+ tools = []
115
+ for pb_tool in pb_agent.tools:
116
+ params = [
117
+ Parameter(name=p.name, description=p.description, type=p.type)
118
+ for p in pb_tool.parameters
119
+ ]
120
+ tools.append(
121
+ Tool(
122
+ id=None, # Protobuf Tool message doesn't have an id field
123
+ name=pb_tool.name,
124
+ description=pb_tool.description,
125
+ parameters=params,
126
+ response=pb_tool.response if pb_tool.response else None,
127
+ )
128
+ )
129
+
130
+ return Agent(
131
+ id=pb_agent.id,
132
+ provider_id=pb_agent.provider_id,
133
+ name=pb_agent.name,
134
+ description=pb_agent.description,
135
+ instruction=pb_agent.instruction,
136
+ tools=tools,
137
+ )
138
+
139
+
140
+ def _convert_pb_trajectory_status_to_sdk(
141
+ pb_status: primitives_pb2.TrajectoryStatus,
142
+ ) -> TrajectoryStatus:
143
+ """Convert protobuf TrajectoryStatus to SDK TrajectoryStatus."""
144
+ status_map = {
145
+ primitives_pb2.TRAJECTORY_STATUS_UNSPECIFIED: TrajectoryStatus.UNKNOWN,
146
+ primitives_pb2.TRAJECTORY_STATUS_PENDING: TrajectoryStatus.PENDING,
147
+ primitives_pb2.TRAJECTORY_STATUS_RUNNING: TrajectoryStatus.RUNNING,
148
+ primitives_pb2.TRAJECTORY_STATUS_COMPLETED: TrajectoryStatus.COMPLETED,
149
+ primitives_pb2.TRAJECTORY_STATUS_SUSPENDED: TrajectoryStatus.SUSPENDED,
150
+ primitives_pb2.TRAJECTORY_STATUS_FAILED: TrajectoryStatus.FAILED,
151
+ }
152
+ return status_map[pb_status]
153
+
154
+
155
+ def _convert_sdk_trajectory_status_to_pb(
156
+ status: TrajectoryStatus,
157
+ ) -> primitives_pb2.TrajectoryStatus:
158
+ """Convert SDK TrajectoryStatus to protobuf TrajectoryStatus."""
159
+ status_map = {
160
+ TrajectoryStatus.UNKNOWN: primitives_pb2.TRAJECTORY_STATUS_UNSPECIFIED,
161
+ TrajectoryStatus.PENDING: primitives_pb2.TRAJECTORY_STATUS_PENDING,
162
+ TrajectoryStatus.RUNNING: primitives_pb2.TRAJECTORY_STATUS_RUNNING,
163
+ TrajectoryStatus.COMPLETED: primitives_pb2.TRAJECTORY_STATUS_COMPLETED,
164
+ TrajectoryStatus.SUSPENDED: primitives_pb2.TRAJECTORY_STATUS_SUSPENDED,
165
+ TrajectoryStatus.FAILED: primitives_pb2.TRAJECTORY_STATUS_FAILED,
166
+ }
167
+ return status_map[status]
168
+
169
+
170
+ def _convert_pb_stage_to_sdk(pb_stage: primitives_pb2.Stage) -> Stage:
171
+ """Convert protobuf Stage to SDK Stage."""
172
+ stage_map = {
173
+ primitives_pb2.STAGE_PRE_RUN: Stage.PRE_RUN,
174
+ primitives_pb2.STAGE_PRE_MODEL: Stage.PRE_MODEL,
175
+ primitives_pb2.STAGE_POST_MODEL: Stage.POST_MODEL,
176
+ primitives_pb2.STAGE_PRE_TOOL: Stage.PRE_TOOL,
177
+ primitives_pb2.STAGE_POST_TOOL: Stage.POST_TOOL,
178
+ primitives_pb2.STAGE_POST_RUN: Stage.POST_RUN,
179
+ }
180
+ return stage_map[pb_stage]
181
+
182
+
183
+ def _convert_pb_role_to_sdk(pb_role: primitives_pb2.Role) -> Role:
184
+ """Convert protobuf Role to SDK Role."""
185
+ role_map = {
186
+ primitives_pb2.ROLE_USER: Role.USER,
187
+ primitives_pb2.ROLE_MODEL: Role.MODEL,
188
+ primitives_pb2.ROLE_TOOL: Role.TOOL,
189
+ primitives_pb2.ROLE_SYSTEM: Role.SYSTEM,
190
+ }
191
+ return role_map[pb_role]
192
+
193
+
194
+ def _convert_pb_content_to_sdk(pb_content: primitives_pb2.Content) -> Content:
195
+ """Convert protobuf Content to SDK Content."""
196
+ if pb_content.HasField("prompt"):
197
+ return PromptContent(text=pb_content.prompt.text)
198
+ elif pb_content.HasField("tool_request"):
199
+ # Convert protobuf Value to Python dict
200
+ args = {}
201
+ if pb_content.tool_request.HasField("args"):
202
+ args = MessageToDict(
203
+ pb_content.tool_request.args, preserving_proto_field_name=True
204
+ )
205
+ if not isinstance(args, dict):
206
+ args = {}
207
+ return ToolRequestContent(tool_id=pb_content.tool_request.tool_id, args=args)
208
+ elif pb_content.HasField("tool_response"):
209
+ # Convert protobuf Value to Python value
210
+ response = MessageToDict(
211
+ pb_content.tool_response.response, preserving_proto_field_name=True
212
+ )
213
+ return ToolResponseContent(
214
+ tool_id=pb_content.tool_response.tool_id, response=response
215
+ )
216
+ else:
217
+ raise ValueError(
218
+ f"Unsupported protobuf content type: {pb_content.WhichOneof('content_type')}"
219
+ )
220
+
221
+
222
+ def _convert_pb_trajectory_step_to_sdk(
223
+ pb_step: primitives_pb2.TrajectoryStep,
224
+ ) -> TrajectoryStep:
225
+ """Convert protobuf TrajectoryStep to SDK TrajectoryStep."""
226
+ created_at = (
227
+ datetime.fromtimestamp(pb_step.created_at.seconds, tz=UTC)
228
+ if pb_step.HasField("created_at")
229
+ else datetime.now(tz=UTC)
230
+ )
231
+
232
+ content = (
233
+ _convert_pb_content_to_sdk(pb_step.content)
234
+ if pb_step.HasField("content")
235
+ else None
236
+ )
237
+
238
+ return TrajectoryStep(
239
+ role=_convert_pb_role_to_sdk(pb_step.role),
240
+ stage=_convert_pb_stage_to_sdk(pb_step.stage),
241
+ content=content,
242
+ created_at=created_at,
243
+ state={}, # State is not in protobuf TrajectoryStep
244
+ context=None, # Context is not in protobuf TrajectoryStep
245
+ )
246
+
247
+
248
+ def _convert_pb_adjudicated_step_to_sdk(
249
+ pb_adjudicated_step: primitives_pb2.AdjudicatedStep,
250
+ ) -> AdjudicatedStep:
251
+ """Convert protobuf AdjudicatedStep to SDK AdjudicatedStep."""
252
+ # Convert the step
253
+ pb_step = pb_adjudicated_step.step if pb_adjudicated_step.HasField("step") else None
254
+ if pb_step is None:
255
+ raise ValueError("AdjudicatedStep must have a step field")
256
+
257
+ trajectory_step = _convert_pb_trajectory_step_to_sdk(pb_step)
258
+
259
+ # Convert adjudication
260
+ pb_adjudication = (
261
+ pb_adjudicated_step.adjudication
262
+ if pb_adjudicated_step.HasField("adjudication")
263
+ else None
264
+ )
265
+ if pb_adjudication is None:
266
+ raise ValueError("AdjudicatedStep must have an adjudication field")
267
+ adjudication = _convert_pb_adjudication_to_sdk(pb_adjudication)
268
+
269
+ # Default mode to GOVERN since protobuf doesn't have this field
270
+ # In practice, this should be set based on the policy engine configuration
271
+ mode = PolicyEngineMode.GOVERN
272
+
273
+ return AdjudicatedStep(mode=mode, adjudication=adjudication, step=trajectory_step)
274
+
275
+
276
+ def _convert_pb_trajectory_to_sdk(
277
+ pb_trajectory: primitives_pb2.Trajectory,
278
+ ) -> Trajectory:
279
+ """Convert protobuf Trajectory to SDK Trajectory."""
280
+ # Convert timestamps
281
+ created_at = (
282
+ datetime.fromtimestamp(pb_trajectory.created_at.seconds, tz=UTC)
283
+ if pb_trajectory.HasField("created_at")
284
+ else datetime.now(tz=UTC)
285
+ )
286
+ updated_at = (
287
+ datetime.fromtimestamp(pb_trajectory.updated_at.seconds, tz=UTC)
288
+ if pb_trajectory.HasField("updated_at")
289
+ else datetime.now(tz=UTC)
290
+ )
291
+ started_at = (
292
+ datetime.fromtimestamp(pb_trajectory.started_at.seconds, tz=UTC)
293
+ if pb_trajectory.HasField("started_at")
294
+ else None
295
+ )
296
+ ended_at = (
297
+ datetime.fromtimestamp(pb_trajectory.ended_at.seconds, tz=UTC)
298
+ if pb_trajectory.HasField("ended_at")
299
+ else None
300
+ )
301
+
302
+ # Convert metadata (protobuf map<string, Value> to dict)
303
+ metadata = {
304
+ key: MessageToDict(value, preserving_proto_field_name=True)
305
+ for key, value in pb_trajectory.metadata.items()
306
+ }
307
+
308
+ return Trajectory(
309
+ id=pb_trajectory.id,
310
+ agent_id=pb_trajectory.agent_id,
311
+ status=_convert_pb_trajectory_status_to_sdk(pb_trajectory.status),
312
+ metadata=metadata,
313
+ created_at=created_at,
314
+ updated_at=updated_at,
315
+ started_at=started_at,
316
+ ended_at=ended_at,
317
+ steps=[], # Steps are not included in the basic trajectory response
318
+ )
319
+
320
+
321
+ def _convert_pb_adjudicated_trajectory_to_sdk(
322
+ pb_response: harness_pb2.GetTrajectoryResponse,
323
+ ) -> AdjudicatedTrajectory:
324
+ """Convert protobuf GetTrajectoryResponse to SDK AdjudicatedTrajectory."""
325
+ # Convert the trajectory
326
+ pb_trajectory = (
327
+ pb_response.trajectory if pb_response.HasField("trajectory") else None
328
+ )
329
+ if pb_trajectory is None:
330
+ raise ValueError("GetTrajectoryResponse must have a trajectory field")
331
+
332
+ trajectory = _convert_pb_trajectory_to_sdk(pb_trajectory)
333
+
334
+ # Convert adjudicated steps
335
+ adjudicated_steps = [
336
+ _convert_pb_adjudicated_step_to_sdk(pb_step) for pb_step in pb_response.steps
337
+ ]
338
+
339
+ # Exclude steps from trajectory.model_dump() since AdjudicatedTrajectory uses AdjudicatedStep instead
340
+ trajectory_dict = trajectory.model_dump(exclude={"steps"})
341
+ return AdjudicatedTrajectory(**trajectory_dict, steps=adjudicated_steps)
342
+
343
+
344
+ def _convert_pb_adjudication_record_to_sdk(
345
+ pb_record: harness_pb2.AdjudicationRecord,
346
+ ) -> AdjudicationRecord:
347
+ """Convert protobuf AdjudicationRecord to SDK AdjudicationRecord."""
348
+ adjudication = _convert_pb_adjudication_to_sdk(pb_record.adjudication)
349
+ return AdjudicationRecord(
350
+ agent_id=pb_record.agent_id,
351
+ trajectory_id=pb_record.trajectory_id,
352
+ step_id=pb_record.step_id,
353
+ adjudication=adjudication,
354
+ )