uipath-openai-agents 0.0.1__py3-none-any.whl → 0.0.3__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.
@@ -1,15 +1,10 @@
1
1
  """Runtime class for executing OpenAI Agents within the UiPath framework."""
2
2
 
3
3
  import json
4
- import os
5
4
  from typing import Any, AsyncGenerator
6
5
  from uuid import uuid4
7
6
 
8
- from agents import (
9
- Agent,
10
- Runner,
11
- SQLiteSession,
12
- )
7
+ from agents import Agent, Runner
13
8
  from uipath.runtime import (
14
9
  UiPathExecuteOptions,
15
10
  UiPathRuntimeResult,
@@ -27,7 +22,6 @@ from uipath.runtime.schema import UiPathRuntimeSchema
27
22
  from ._serialize import serialize_output
28
23
  from .errors import UiPathOpenAIAgentsErrorCode, UiPathOpenAIAgentsRuntimeError
29
24
  from .schema import get_agent_schema, get_entrypoints_schema
30
- from .storage import SqliteAgentStorage
31
25
 
32
26
 
33
27
  class UiPathOpenAIAgentRuntime:
@@ -40,9 +34,6 @@ class UiPathOpenAIAgentRuntime:
40
34
  agent: Agent,
41
35
  runtime_id: str | None = None,
42
36
  entrypoint: str | None = None,
43
- storage_path: str | None = None,
44
- loaded_object: Any | None = None,
45
- storage: SqliteAgentStorage | None = None,
46
37
  ):
47
38
  """
48
39
  Initialize the runtime.
@@ -51,86 +42,10 @@ class UiPathOpenAIAgentRuntime:
51
42
  agent: The OpenAI Agent to execute
52
43
  runtime_id: Unique identifier for this runtime instance
53
44
  entrypoint: Optional entrypoint name (for schema generation)
54
- storage_path: Path to SQLite database for session persistence
55
- loaded_object: Original loaded object (for schema inference)
56
- storage: Optional storage instance for state persistence
57
45
  """
58
46
  self.agent: Agent = agent
59
47
  self.runtime_id: str = runtime_id or "default"
60
48
  self.entrypoint: str | None = entrypoint
61
- self.storage_path: str | None = storage_path
62
- self.loaded_object: Any | None = loaded_object
63
- self.storage: SqliteAgentStorage | None = storage
64
-
65
- # Configure OpenAI Agents SDK to use Responses API
66
- # UiPath supports both APIs via X-UiPath-LlmGateway-ApiFlavor header
67
- # Using responses API for enhanced agent capabilities (conversation state, reasoning)
68
- from agents import set_default_openai_api
69
-
70
- set_default_openai_api("responses")
71
-
72
- # Inject UiPath OpenAI client if UiPath credentials are available
73
- self._setup_uipath_client()
74
-
75
- def _setup_uipath_client(self) -> None:
76
- """Set up UiPath OpenAI client for agents to use UiPath gateway.
77
-
78
- This injects the UiPath OpenAI client into the OpenAI Agents SDK
79
- so all agents use the UiPath LLM Gateway instead of direct OpenAI.
80
-
81
- The model is automatically extracted from the agent's `model` parameter.
82
- If not specified in Agent(), the SDK uses agents.models.get_default_model().
83
-
84
- If UiPath credentials are not available, falls back to default OpenAI client.
85
- """
86
- try:
87
- # Import here to avoid circular dependency
88
- from uipath_openai_agents.chat import UiPathChatOpenAI
89
-
90
- # Check if UiPath credentials are available
91
- org_id = os.getenv("UIPATH_ORGANIZATION_ID")
92
- tenant_id = os.getenv("UIPATH_TENANT_ID")
93
- token = os.getenv("UIPATH_ACCESS_TOKEN")
94
- uipath_url = os.getenv("UIPATH_URL")
95
-
96
- if org_id and tenant_id and token and uipath_url:
97
- # Extract model from agent definition
98
- from agents.models import get_default_model
99
-
100
- from uipath_openai_agents.chat.supported_models import OpenAIModels
101
-
102
- if hasattr(self.agent, "model") and self.agent.model:
103
- model_name = str(self.agent.model)
104
- else:
105
- model_name = get_default_model()
106
-
107
- # Normalize generic model names to UiPath-specific versions
108
- model_name = OpenAIModels.normalize_model_name(model_name)
109
-
110
- # Update agent's model to normalized version so SDK sends correct model in body
111
- self.agent.model = model_name
112
-
113
- # Create UiPath OpenAI client
114
- uipath_client = UiPathChatOpenAI(
115
- token=token,
116
- org_id=org_id,
117
- tenant_id=tenant_id,
118
- model_name=model_name,
119
- )
120
-
121
- # Inject into OpenAI Agents SDK
122
- # This makes all agents use UiPath gateway
123
- from agents.models import _openai_shared
124
-
125
- _openai_shared.set_default_openai_client(uipath_client.async_client)
126
-
127
- except ImportError:
128
- # UiPath chat module not available, skip injection
129
- pass
130
- except Exception:
131
- # If injection fails, fall back to default OpenAI client
132
- # Agents will use OPENAI_API_KEY if set
133
- pass
134
49
 
135
50
  async def execute(
136
51
  self,
@@ -207,54 +122,27 @@ class UiPathOpenAIAgentRuntime:
207
122
  Runtime events if stream_events=True, then final result
208
123
  """
209
124
  agent_input = self._prepare_agent_input(input)
210
- is_resuming = bool(options and options.resume)
211
-
212
- # Create session for state persistence (local to this run)
213
- # SQLiteSession automatically loads existing data from the database when created
214
- session: SQLiteSession | None = None
215
- if self.storage_path:
216
- session = SQLiteSession(self.runtime_id, self.storage_path)
217
125
 
218
126
  # Run the agent with streaming if events requested
219
- try:
220
- if stream_events:
221
- # Use streaming for events
222
- async for event_or_result in self._run_agent_streamed(
223
- agent_input, options, stream_events, session
224
- ):
225
- yield event_or_result
226
- else:
227
- # Use non-streaming for simple execution
228
- result = await Runner.run(
229
- starting_agent=self.agent,
230
- input=agent_input,
231
- session=session,
232
- )
233
- yield self._create_success_result(result.final_output)
234
-
235
- except Exception:
236
- # Clean up session on error
237
- if session and self.storage_path and not is_resuming:
238
- # Delete incomplete session
239
- try:
240
- import os
241
-
242
- if os.path.exists(self.storage_path):
243
- os.remove(self.storage_path)
244
- except Exception:
245
- pass # Best effort cleanup
246
- raise
247
- finally:
248
- # Always close session after run completes with proper WAL checkpoint
249
- if session:
250
- self._close_session_with_checkpoint(session)
127
+ if stream_events:
128
+ # Use streaming for events
129
+ async for event_or_result in self._run_agent_streamed(
130
+ agent_input, options, stream_events
131
+ ):
132
+ yield event_or_result
133
+ else:
134
+ # Use non-streaming for simple execution
135
+ result = await Runner.run(
136
+ starting_agent=self.agent,
137
+ input=agent_input,
138
+ )
139
+ yield self._create_success_result(result.final_output)
251
140
 
252
141
  async def _run_agent_streamed(
253
142
  self,
254
143
  agent_input: str | list[Any],
255
144
  options: UiPathExecuteOptions | UiPathStreamOptions | None,
256
145
  stream_events: bool,
257
- session: SQLiteSession | None,
258
146
  ) -> AsyncGenerator[UiPathRuntimeEvent | UiPathRuntimeResult, None]:
259
147
  """
260
148
  Run agent using streaming API to enable event streaming.
@@ -272,7 +160,6 @@ class UiPathOpenAIAgentRuntime:
272
160
  result = Runner.run_streamed(
273
161
  starting_agent=self.agent,
274
162
  input=agent_input,
275
- session=session,
276
163
  )
277
164
 
278
165
  # Stream events from the agent
@@ -335,49 +222,17 @@ class UiPathOpenAIAgentRuntime:
335
222
  """
336
223
  Prepare agent input from UiPath input dictionary.
337
224
 
338
- Supports two input formats:
339
- - {"message": "text"} → returns string for Runner.run()
340
- - {"messages": [...]} → returns list of message dicts for Runner.run()
341
-
342
- Note: When using sessions, string input is preferred as it doesn't
343
- require a session_input_callback.
344
-
345
- Args:
346
- input: Input dictionary from UiPath
347
-
348
- Returns:
349
- String or list for Runner.run() input parameter
350
-
351
- Raises:
352
- ValueError: If input doesn't contain "message" or "messages" field
353
225
  """
354
226
  if not input:
355
- raise ValueError(
356
- "Input is required. Provide either 'message' (string) or 'messages' (list of message dicts)"
357
- )
227
+ return ""
358
228
 
359
- # Check for "messages" field (list of message dicts)
360
- if "messages" in input:
361
- messages = input["messages"]
362
- # Ensure it's a list
363
- if isinstance(messages, list):
364
- return messages
365
- else:
366
- raise ValueError(
367
- "'messages' field must be a list of message dictionaries"
368
- )
229
+ messages = input.get("messages", "")
369
230
 
370
- # Check for "message" field (simple string)
371
- if "message" in input:
372
- message = input["message"]
373
- # Return as string (OpenAI Agents SDK handles string → message conversion)
374
- return str(message)
231
+ if isinstance(messages, (str, list)):
232
+ return messages
375
233
 
376
- # No valid field found
377
- raise ValueError(
378
- "Input must contain either 'message' (string) or 'messages' (list of message dicts). "
379
- f"Got keys: {list(input.keys())}"
380
- )
234
+ # Fallback to empty string for unexpected types
235
+ return ""
381
236
 
382
237
  def _serialize_message(self, message: Any) -> dict[str, Any]:
383
238
  """
@@ -477,7 +332,7 @@ class UiPathOpenAIAgentRuntime:
477
332
  Returns:
478
333
  UiPathRuntimeSchema with input/output schemas and graph structure
479
334
  """
480
- entrypoints_schema = get_entrypoints_schema(self.agent, self.loaded_object)
335
+ entrypoints_schema = get_entrypoints_schema(self.agent)
481
336
 
482
337
  return UiPathRuntimeSchema(
483
338
  filePath=self.entrypoint,
@@ -488,45 +343,6 @@ class UiPathOpenAIAgentRuntime:
488
343
  graph=get_agent_schema(self.agent),
489
344
  )
490
345
 
491
- def _close_session_with_checkpoint(self, session: SQLiteSession) -> None:
492
- """Close SQLite session with WAL checkpoint to release file locks.
493
-
494
- OpenAI SDK uses sync sqlite3 which doesn't release file locks on Windows
495
- without explicit WAL checkpoint. This is especially important for cleanup.
496
-
497
- Args:
498
- session: The SQLiteSession to close
499
- """
500
- try:
501
- # Get the underlying connection
502
- conn = session._get_connection()
503
-
504
- # Commit any pending transactions
505
- try:
506
- conn.commit()
507
- except Exception:
508
- pass # Best effort
509
-
510
- # Force WAL checkpoint to release shared memory files
511
- # This is especially important on Windows
512
- try:
513
- conn.execute("PRAGMA wal_checkpoint(TRUNCATE)")
514
- conn.commit()
515
- except Exception:
516
- pass # Best effort
517
-
518
- except Exception:
519
- pass # Best effort cleanup
520
-
521
- finally:
522
- # Always call the session's close method
523
- try:
524
- session.close()
525
- except Exception:
526
- pass # Best effort
527
-
528
346
  async def dispose(self) -> None:
529
347
  """Cleanup runtime resources."""
530
- # Sessions are closed immediately after each run in _run_agent()
531
- # Storage is shared across runtimes and managed by the factory
532
348
  pass