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
polos/llm/generate.py
ADDED
|
@@ -0,0 +1,152 @@
|
|
|
1
|
+
"""Built-in LLM generation function for executing LLM API calls."""
|
|
2
|
+
|
|
3
|
+
from typing import Any
|
|
4
|
+
|
|
5
|
+
from ..core.context import WorkflowContext
|
|
6
|
+
from ..core.workflow import _execution_context
|
|
7
|
+
from ..middleware.guardrail import GuardrailContext
|
|
8
|
+
from ..middleware.guardrail_executor import execute_guardrails
|
|
9
|
+
from ..middleware.hook import HookAction
|
|
10
|
+
from ..types.types import AgentConfig
|
|
11
|
+
from ..utils.agent import convert_input_to_messages
|
|
12
|
+
from .providers import get_provider
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
async def _llm_generate(ctx: WorkflowContext, payload: dict[str, Any]) -> dict[str, Any]:
|
|
16
|
+
"""
|
|
17
|
+
Durable function for LLM response generation with guardrail support and retry logic.
|
|
18
|
+
|
|
19
|
+
Must be executed within a workflow execution context. Uses step_outputs for durability.
|
|
20
|
+
|
|
21
|
+
Args:
|
|
22
|
+
ctx: WorkflowContext for the current execution
|
|
23
|
+
payload: Dictionary containing:
|
|
24
|
+
- agent_run_id: Optional[str]
|
|
25
|
+
- agent_config: Dict with provider, model, tools, system_prompt, etc.
|
|
26
|
+
- input: Union[str, List[Dict]] - Input data
|
|
27
|
+
- messages: Optional[List[Dict]] - Pre-formatted messages (used for retries)
|
|
28
|
+
- agent_step: int - Step in agent conversation (1 = first, 2 = after tools, etc.)
|
|
29
|
+
- guardrails: Optional[List[Callable]] - List of guardrail callables
|
|
30
|
+
- guardrail_max_retries: int - Max retries for guardrail failures (default: 0)
|
|
31
|
+
|
|
32
|
+
Returns:
|
|
33
|
+
Dictionary with LLM result containing content, tool_calls, usage, etc.
|
|
34
|
+
"""
|
|
35
|
+
# Check we're in a workflow execution context
|
|
36
|
+
exec_context = _execution_context.get()
|
|
37
|
+
if not exec_context or not exec_context.get("execution_id"):
|
|
38
|
+
raise ValueError("_llm_generate must be executed within a workflow execution context")
|
|
39
|
+
|
|
40
|
+
# Extract payload
|
|
41
|
+
agent_run_id = payload.get("agent_run_id")
|
|
42
|
+
agent_config = AgentConfig.model_validate(payload.get("agent_config"))
|
|
43
|
+
input_data = payload.get("input")
|
|
44
|
+
agent_step = payload.get("agent_step", 1)
|
|
45
|
+
guardrails = payload.get("guardrails") # List of guardrail callables
|
|
46
|
+
guardrail_max_retries = payload.get("guardrail_max_retries", 2)
|
|
47
|
+
tool_results = payload.get("tool_results") # Optional tool results in OpenAI format
|
|
48
|
+
|
|
49
|
+
# Get LLM provider
|
|
50
|
+
provider_kwargs = {}
|
|
51
|
+
if agent_config.provider_base_url:
|
|
52
|
+
provider_kwargs["base_url"] = agent_config.provider_base_url
|
|
53
|
+
if agent_config.provider_llm_api:
|
|
54
|
+
provider_kwargs["llm_api"] = agent_config.provider_llm_api
|
|
55
|
+
provider = get_provider(agent_config.provider, **provider_kwargs)
|
|
56
|
+
|
|
57
|
+
# Convert input to messages (without system_prompt - provider will handle it)
|
|
58
|
+
messages = convert_input_to_messages(input_data, system_prompt=None)
|
|
59
|
+
|
|
60
|
+
guardrail_retry_count = 0
|
|
61
|
+
|
|
62
|
+
# Guardrail retry loop
|
|
63
|
+
while guardrail_retry_count <= guardrail_max_retries:
|
|
64
|
+
# Call the LLM API via provider using step.run() for durable execution
|
|
65
|
+
# Pass agent_config and tool_results to provider
|
|
66
|
+
# Include provider_kwargs if provided
|
|
67
|
+
provider_kwargs = agent_config.provider_kwargs or {}
|
|
68
|
+
|
|
69
|
+
llm_response = await ctx.step.run(
|
|
70
|
+
f"llm_generate:{agent_step}",
|
|
71
|
+
provider.generate,
|
|
72
|
+
messages=messages,
|
|
73
|
+
model=agent_config.model,
|
|
74
|
+
tools=agent_config.tools,
|
|
75
|
+
temperature=agent_config.temperature,
|
|
76
|
+
max_tokens=agent_config.max_output_tokens,
|
|
77
|
+
top_p=agent_config.top_p,
|
|
78
|
+
agent_config=agent_config.model_dump(mode="json"),
|
|
79
|
+
tool_results=tool_results,
|
|
80
|
+
output_schema=agent_config.output_schema,
|
|
81
|
+
output_schema_name=agent_config.output_schema_name,
|
|
82
|
+
**provider_kwargs,
|
|
83
|
+
)
|
|
84
|
+
response_content = llm_response.content
|
|
85
|
+
response_tool_calls = llm_response.tool_calls if llm_response.tool_calls else None
|
|
86
|
+
usage = llm_response.usage if llm_response.usage else None
|
|
87
|
+
raw_output = llm_response.raw_output if llm_response.raw_output else None
|
|
88
|
+
|
|
89
|
+
llm_result = {
|
|
90
|
+
"agent_run_id": agent_run_id,
|
|
91
|
+
"status": "completed",
|
|
92
|
+
"content": response_content,
|
|
93
|
+
"tool_calls": response_tool_calls,
|
|
94
|
+
"usage": usage,
|
|
95
|
+
"raw_output": raw_output,
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
# If no guardrails, return immediately
|
|
99
|
+
if not guardrails:
|
|
100
|
+
return llm_result
|
|
101
|
+
|
|
102
|
+
# Execute guardrails on the LLM result
|
|
103
|
+
guardrail_context = GuardrailContext(
|
|
104
|
+
content=llm_result.get("content"),
|
|
105
|
+
tool_calls=llm_result.get("tool_calls"),
|
|
106
|
+
agent_workflow_id=None,
|
|
107
|
+
agent_run_id=agent_run_id,
|
|
108
|
+
llm_config=agent_config,
|
|
109
|
+
)
|
|
110
|
+
|
|
111
|
+
# Execute guardrails using the existing execute_guardrails function
|
|
112
|
+
guardrail_result = await execute_guardrails(
|
|
113
|
+
f"{agent_step}.guardrail",
|
|
114
|
+
guardrails,
|
|
115
|
+
guardrail_context,
|
|
116
|
+
ctx,
|
|
117
|
+
agent_config=agent_config,
|
|
118
|
+
)
|
|
119
|
+
|
|
120
|
+
# Check guardrail result
|
|
121
|
+
if guardrail_result.action == HookAction.FAIL:
|
|
122
|
+
# Guardrail failed - check if we can retry
|
|
123
|
+
guardrail_error_message = (
|
|
124
|
+
guardrail_result.error_message or "Guardrail validation failed"
|
|
125
|
+
)
|
|
126
|
+
if guardrail_retry_count >= guardrail_max_retries:
|
|
127
|
+
# Exhausted retries - raise exception with error message
|
|
128
|
+
raise Exception(
|
|
129
|
+
f"Guardrail failed after {guardrail_max_retries} retries. "
|
|
130
|
+
f"Last error: {guardrail_error_message}"
|
|
131
|
+
)
|
|
132
|
+
|
|
133
|
+
# Add feedback to messages for retry
|
|
134
|
+
feedback_message = (
|
|
135
|
+
f"Previous attempt failed guardrail validation: "
|
|
136
|
+
f"{guardrail_error_message}. Please revise your response "
|
|
137
|
+
f"accordingly."
|
|
138
|
+
)
|
|
139
|
+
messages.append({"role": "user", "content": feedback_message})
|
|
140
|
+
guardrail_retry_count += 1
|
|
141
|
+
continue # Retry LLM generation
|
|
142
|
+
|
|
143
|
+
# CONTINUE - all guardrails passed, apply accumulated modifications
|
|
144
|
+
if guardrail_result.modified_content is not None:
|
|
145
|
+
llm_result["content"] = guardrail_result.modified_content
|
|
146
|
+
if guardrail_result.modified_tool_calls is not None:
|
|
147
|
+
llm_result["tool_calls"] = guardrail_result.modified_tool_calls
|
|
148
|
+
|
|
149
|
+
return llm_result
|
|
150
|
+
|
|
151
|
+
# Should not reach here, but just in case
|
|
152
|
+
raise Exception(f"Failed to generate valid response after {guardrail_max_retries} retries")
|