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.
- polos/__init__.py +105 -0
- polos/agents/__init__.py +7 -0
- polos/agents/agent.py +746 -0
- polos/agents/conversation_history.py +121 -0
- polos/agents/stop_conditions.py +280 -0
- polos/agents/stream.py +635 -0
- polos/core/__init__.py +0 -0
- polos/core/context.py +143 -0
- polos/core/state.py +26 -0
- polos/core/step.py +1380 -0
- polos/core/workflow.py +1192 -0
- polos/features/__init__.py +0 -0
- polos/features/events.py +456 -0
- polos/features/schedules.py +110 -0
- polos/features/tracing.py +605 -0
- polos/features/wait.py +82 -0
- polos/llm/__init__.py +9 -0
- polos/llm/generate.py +152 -0
- polos/llm/providers/__init__.py +5 -0
- polos/llm/providers/anthropic.py +615 -0
- polos/llm/providers/azure.py +42 -0
- polos/llm/providers/base.py +196 -0
- polos/llm/providers/fireworks.py +41 -0
- polos/llm/providers/gemini.py +40 -0
- polos/llm/providers/groq.py +40 -0
- polos/llm/providers/openai.py +1021 -0
- polos/llm/providers/together.py +40 -0
- polos/llm/stream.py +183 -0
- polos/middleware/__init__.py +0 -0
- polos/middleware/guardrail.py +148 -0
- polos/middleware/guardrail_executor.py +253 -0
- polos/middleware/hook.py +164 -0
- polos/middleware/hook_executor.py +104 -0
- polos/runtime/__init__.py +0 -0
- polos/runtime/batch.py +87 -0
- polos/runtime/client.py +841 -0
- polos/runtime/queue.py +42 -0
- polos/runtime/worker.py +1365 -0
- polos/runtime/worker_server.py +249 -0
- polos/tools/__init__.py +0 -0
- polos/tools/tool.py +587 -0
- polos/types/__init__.py +23 -0
- polos/types/types.py +116 -0
- polos/utils/__init__.py +27 -0
- polos/utils/agent.py +27 -0
- polos/utils/client_context.py +41 -0
- polos/utils/config.py +12 -0
- polos/utils/output_schema.py +311 -0
- polos/utils/retry.py +47 -0
- polos/utils/serializer.py +167 -0
- polos/utils/tracing.py +27 -0
- polos/utils/worker_singleton.py +40 -0
- polos_sdk-0.1.0.dist-info/METADATA +650 -0
- polos_sdk-0.1.0.dist-info/RECORD +55 -0
- 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
|