polos-sdk 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 (55) hide show
  1. polos/__init__.py +105 -0
  2. polos/agents/__init__.py +7 -0
  3. polos/agents/agent.py +746 -0
  4. polos/agents/conversation_history.py +121 -0
  5. polos/agents/stop_conditions.py +280 -0
  6. polos/agents/stream.py +635 -0
  7. polos/core/__init__.py +0 -0
  8. polos/core/context.py +143 -0
  9. polos/core/state.py +26 -0
  10. polos/core/step.py +1380 -0
  11. polos/core/workflow.py +1192 -0
  12. polos/features/__init__.py +0 -0
  13. polos/features/events.py +456 -0
  14. polos/features/schedules.py +110 -0
  15. polos/features/tracing.py +605 -0
  16. polos/features/wait.py +82 -0
  17. polos/llm/__init__.py +9 -0
  18. polos/llm/generate.py +152 -0
  19. polos/llm/providers/__init__.py +5 -0
  20. polos/llm/providers/anthropic.py +615 -0
  21. polos/llm/providers/azure.py +42 -0
  22. polos/llm/providers/base.py +196 -0
  23. polos/llm/providers/fireworks.py +41 -0
  24. polos/llm/providers/gemini.py +40 -0
  25. polos/llm/providers/groq.py +40 -0
  26. polos/llm/providers/openai.py +1021 -0
  27. polos/llm/providers/together.py +40 -0
  28. polos/llm/stream.py +183 -0
  29. polos/middleware/__init__.py +0 -0
  30. polos/middleware/guardrail.py +148 -0
  31. polos/middleware/guardrail_executor.py +253 -0
  32. polos/middleware/hook.py +164 -0
  33. polos/middleware/hook_executor.py +104 -0
  34. polos/runtime/__init__.py +0 -0
  35. polos/runtime/batch.py +87 -0
  36. polos/runtime/client.py +841 -0
  37. polos/runtime/queue.py +42 -0
  38. polos/runtime/worker.py +1365 -0
  39. polos/runtime/worker_server.py +249 -0
  40. polos/tools/__init__.py +0 -0
  41. polos/tools/tool.py +587 -0
  42. polos/types/__init__.py +23 -0
  43. polos/types/types.py +116 -0
  44. polos/utils/__init__.py +27 -0
  45. polos/utils/agent.py +27 -0
  46. polos/utils/client_context.py +41 -0
  47. polos/utils/config.py +12 -0
  48. polos/utils/output_schema.py +311 -0
  49. polos/utils/retry.py +47 -0
  50. polos/utils/serializer.py +167 -0
  51. polos/utils/tracing.py +27 -0
  52. polos/utils/worker_singleton.py +40 -0
  53. polos_sdk-0.1.0.dist-info/METADATA +650 -0
  54. polos_sdk-0.1.0.dist-info/RECORD +55 -0
  55. polos_sdk-0.1.0.dist-info/WHEEL +4 -0
@@ -0,0 +1,167 @@
1
+ """JSON serialization utilities for handling non-serializable types."""
2
+
3
+ import json
4
+ from typing import Any
5
+
6
+ from pydantic import BaseModel
7
+
8
+ from ..types.types import AgentResult
9
+
10
+
11
+ def is_json_serializable(obj: Any) -> bool:
12
+ """Check if an object is JSON serializable by attempting json.dumps.
13
+
14
+ Args:
15
+ obj: Object to check
16
+
17
+ Returns:
18
+ True if the object is JSON serializable, False otherwise
19
+ """
20
+ try:
21
+ json.dumps(obj)
22
+ return True
23
+ except (TypeError, ValueError):
24
+ return False
25
+
26
+
27
+ def serialize(obj: Any) -> Any:
28
+ """Serialize an object to a JSON-serializable object.
29
+
30
+ If the input is a Pydantic BaseModel, uses model_dump(mode="json") to serialize it to a dict.
31
+ Otherwise, checks if it's JSON serializable via json.dumps and raises TypeError if not.
32
+
33
+ Args:
34
+ obj: Object to serialize
35
+
36
+ Returns:
37
+ JSON-serializable value (dict, list, str, int, float, bool, None)
38
+
39
+ Raises:
40
+ TypeError: If the object is not a Pydantic model and not JSON serializable
41
+ """
42
+ # Handle Pydantic models
43
+ if isinstance(obj, BaseModel):
44
+ return obj.model_dump(mode="json")
45
+
46
+ # Check if it's JSON serializable
47
+ if not is_json_serializable(obj):
48
+ raise TypeError(
49
+ f"Object of type {type(obj).__name__} is not JSON serializable. "
50
+ f"If it's a Pydantic model, ensure it inherits from BaseModel."
51
+ )
52
+
53
+ return obj
54
+
55
+
56
+ def json_serialize(obj: Any) -> str:
57
+ """Serialize an object to a JSON string.
58
+
59
+ If the input is a Pydantic BaseModel, uses model_dump_json() to serialize it to a JSON string.
60
+ Otherwise, uses json.dumps and raises TypeError if not.
61
+
62
+ Args:
63
+ obj: Object to serialize
64
+
65
+ Returns:
66
+ JSON string
67
+
68
+ Raises:
69
+ TypeError: If the object is not a Pydantic model and not JSON serializable
70
+ """
71
+ # Handle Pydantic models
72
+ if isinstance(obj, BaseModel):
73
+ return obj.model_dump_json()
74
+
75
+ # Check if it's JSON serializable
76
+ try:
77
+ return json.dumps(obj)
78
+ except (TypeError, ValueError) as e:
79
+ raise TypeError(
80
+ f"Object of type {type(obj).__name__} is not JSON serializable. "
81
+ f"If it's a Pydantic model, ensure it inherits from BaseModel."
82
+ ) from e
83
+
84
+
85
+ async def deserialize(obj: Any, output_schema_name: str | None = None) -> Any:
86
+ """Deserialize an object from a JSON string.
87
+
88
+ Args:
89
+ obj: Object to deserialize
90
+ output_schema_name: The name of the output schema
91
+
92
+ Returns:
93
+ Deserialized object
94
+ """
95
+ # If output_schema_name is present, try to reconstruct the Pydantic model
96
+ if output_schema_name and isinstance(obj, dict):
97
+ try:
98
+ # Dynamically import the Pydantic class
99
+ module_path, class_name = output_schema_name.rsplit(".", 1)
100
+ module = __import__(module_path, fromlist=[class_name])
101
+ model_class = getattr(module, class_name)
102
+
103
+ # Validate the dict back to the Pydantic model
104
+ if issubclass(model_class, BaseModel):
105
+ obj = model_class.model_validate(obj)
106
+ except (ImportError, AttributeError, ValueError, TypeError) as e:
107
+ raise Exception(
108
+ f"Failed to reconstruct Pydantic model from output_schema_name: "
109
+ f"{output_schema_name}. Error: {str(e)}"
110
+ ) from e
111
+ return obj
112
+
113
+
114
+ async def deserialize_agent_result(result: AgentResult) -> AgentResult:
115
+ """Deserialize an agent result to a Pydantic model."""
116
+
117
+ # Convert result to structured output schema
118
+ if result.result_schema and result.result is not None:
119
+ result.result = await _deserialize_agent_result_schema(result.result, result.result_schema)
120
+
121
+ # Convert tool results to structured output schema
122
+ for tool_result in result.tool_results:
123
+ if tool_result.result_schema and tool_result.result is not None:
124
+ tool_result.result = await _deserialize_agent_result_schema(
125
+ tool_result.result, tool_result.result_schema
126
+ )
127
+
128
+ return result
129
+
130
+
131
+ async def _deserialize_agent_result_schema(result, schema: str):
132
+ try:
133
+ # Dynamically import the Pydantic class
134
+ module_path, class_name = schema.rsplit(".", 1)
135
+ module = __import__(module_path, fromlist=[class_name])
136
+ model_class = getattr(module, class_name)
137
+
138
+ # Validate that it's a Pydantic BaseModel
139
+ if issubclass(model_class, BaseModel):
140
+ if isinstance(result, str):
141
+ return model_class.model_validate_json(result)
142
+ elif isinstance(result, dict):
143
+ return model_class.model_validate(result)
144
+ else:
145
+ return result
146
+ except (ImportError, AttributeError, ValueError, TypeError) as e:
147
+ # If reconstruction fails, log warning but return dict
148
+ # This allows backward compatibility if the class is not available
149
+ import warnings
150
+
151
+ warnings.warn(
152
+ f"Failed to reconstruct Pydantic model '{schema}': {e}. Returning dict instead.",
153
+ UserWarning,
154
+ stacklevel=2,
155
+ )
156
+ return result
157
+
158
+
159
+ def safe_serialize(value):
160
+ """Serialize with fallback for non-serializable values."""
161
+ try:
162
+ return serialize(value)
163
+ except (TypeError, ValueError):
164
+ # Fallback representations
165
+ if hasattr(value, "__name__"):
166
+ return f"<{value.__name__}>"
167
+ return f"<{type(value).__name__}>"
polos/utils/tracing.py ADDED
@@ -0,0 +1,27 @@
1
+ from opentelemetry.trace import NonRecordingSpan, set_span_in_context
2
+
3
+
4
+ def get_parent_span_context_from_execution_context(exec_context):
5
+ parent_context = None
6
+ if exec_context:
7
+ parent_span_context = exec_context.get("_otel_span_context")
8
+ if parent_span_context:
9
+ # Convert SpanContext to Context by wrapping in NonRecordingSpan
10
+ parent_span = NonRecordingSpan(parent_span_context)
11
+ parent_context = set_span_in_context(parent_span)
12
+
13
+ return parent_context
14
+
15
+
16
+ def get_span_context_from_execution_context(exec_context):
17
+ if exec_context:
18
+ return exec_context.get("_otel_span_context")
19
+ return None
20
+
21
+
22
+ def set_span_context_in_execution_context(exec_context, span_context):
23
+ if exec_context:
24
+ exec_context["_otel_span_context"] = span_context
25
+ if span_context is not None:
26
+ exec_context["_otel_trace_id"] = format(span_context.trace_id, "032x")
27
+ exec_context["_otel_span_id"] = format(span_context.span_id, "016x")
@@ -0,0 +1,40 @@
1
+ """Singleton worker instance management for HTTP client reuse."""
2
+
3
+ from typing import Any
4
+
5
+ import httpx
6
+
7
+ # Singleton worker instance (set by Worker when it starts)
8
+ _current_worker: Any | None = None
9
+
10
+
11
+ def get_worker_client() -> httpx.AsyncClient | None:
12
+ """Get the HTTP client from the current worker instance if available.
13
+
14
+ Returns:
15
+ The worker's HTTP client if a worker is running, None otherwise
16
+ """
17
+ global _current_worker
18
+ if _current_worker is not None and _current_worker.client is not None:
19
+ return _current_worker.client
20
+ return None
21
+
22
+
23
+ def set_current_worker(worker: Any | None) -> None:
24
+ """Set the current worker instance (called by Worker when it starts/stops).
25
+
26
+ Args:
27
+ worker: The Worker instance, or None to clear
28
+ """
29
+ global _current_worker
30
+ _current_worker = worker
31
+
32
+
33
+ def get_current_worker() -> Any | None:
34
+ """Get the current worker instance if available.
35
+
36
+ Returns:
37
+ The current Worker instance, or None if no worker is running
38
+ """
39
+ global _current_worker
40
+ return _current_worker