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
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")
@@ -0,0 +1,5 @@
1
+ """LLM provider implementations."""
2
+
3
+ from .base import LLMProvider, LLMResponse, get_provider, register_provider
4
+
5
+ __all__ = ["LLMProvider", "LLMResponse", "get_provider", "register_provider"]