agent-framework-openai 1.0.0rc6__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.
@@ -0,0 +1,87 @@
1
+ # Copyright (c) Microsoft. All rights reserved.
2
+
3
+ """OpenAI integration for Microsoft Agent Framework.
4
+
5
+ This package provides OpenAI client implementations for the Agent Framework,
6
+ including clients for the Responses API and Chat Completions API.
7
+ """
8
+
9
+ import importlib.metadata
10
+ import sys
11
+
12
+ if sys.version_info >= (3, 13):
13
+ from warnings import deprecated # type: ignore # pragma: no cover
14
+ else:
15
+ from typing_extensions import deprecated # type: ignore # pragma: no cover
16
+
17
+ from ._assistant_provider import OpenAIAssistantProvider
18
+ from ._assistants_client import (
19
+ AssistantToolResources,
20
+ OpenAIAssistantsClient, # type: ignore[reportDeprecated]
21
+ OpenAIAssistantsOptions,
22
+ )
23
+ from ._chat_client import (
24
+ OpenAIChatClient,
25
+ OpenAIChatOptions,
26
+ OpenAIContinuationToken,
27
+ RawOpenAIChatClient,
28
+ )
29
+ from ._chat_completion_client import (
30
+ OpenAIChatCompletionClient,
31
+ OpenAIChatCompletionOptions,
32
+ RawOpenAIChatCompletionClient,
33
+ )
34
+ from ._embedding_client import OpenAIEmbeddingClient, OpenAIEmbeddingOptions
35
+ from ._exceptions import ContentFilterResultSeverity, OpenAIContentFilterException
36
+ from ._shared import OpenAISettings
37
+
38
+ try:
39
+ __version__ = importlib.metadata.version("agent-framework-openai")
40
+ except importlib.metadata.PackageNotFoundError:
41
+ __version__ = "0.0.0" # Fallback for development mode
42
+
43
+ # Deprecated aliases for old names — use subclasses so the warning only fires for the alias
44
+
45
+
46
+ @deprecated(
47
+ "OpenAIResponsesClient is deprecated, use OpenAIChatClient instead.",
48
+ category=DeprecationWarning,
49
+ )
50
+ class OpenAIResponsesClient(OpenAIChatClient): # type: ignore[misc]
51
+ """Deprecated alias for :class:`OpenAIChatClient`."""
52
+
53
+
54
+ @deprecated(
55
+ "RawOpenAIResponsesClient is deprecated, use RawOpenAIChatClient instead.",
56
+ category=DeprecationWarning,
57
+ )
58
+ class RawOpenAIResponsesClient(RawOpenAIChatClient): # type: ignore[misc]
59
+ """Deprecated alias for :class:`RawOpenAIChatClient`."""
60
+
61
+
62
+ OpenAIResponsesOptions = OpenAIChatOptions
63
+ """Deprecated alias for :class:`OpenAIChatOptions`."""
64
+
65
+
66
+ __all__ = [
67
+ "AssistantToolResources",
68
+ "ContentFilterResultSeverity",
69
+ "OpenAIAssistantProvider",
70
+ "OpenAIAssistantsClient",
71
+ "OpenAIAssistantsOptions",
72
+ "OpenAIChatClient",
73
+ "OpenAIChatCompletionClient",
74
+ "OpenAIChatCompletionOptions",
75
+ "OpenAIChatOptions",
76
+ "OpenAIContentFilterException",
77
+ "OpenAIContinuationToken",
78
+ "OpenAIEmbeddingClient",
79
+ "OpenAIEmbeddingOptions",
80
+ "OpenAIResponsesClient",
81
+ "OpenAIResponsesOptions",
82
+ "OpenAISettings",
83
+ "RawOpenAIChatClient",
84
+ "RawOpenAIChatCompletionClient",
85
+ "RawOpenAIResponsesClient",
86
+ "__version__",
87
+ ]
@@ -0,0 +1,564 @@
1
+ # Copyright (c) Microsoft. All rights reserved.
2
+
3
+ from __future__ import annotations
4
+
5
+ import sys
6
+ from collections.abc import Awaitable, Callable, Mapping, MutableMapping, Sequence
7
+ from typing import TYPE_CHECKING, Any, Generic, cast
8
+
9
+ from agent_framework._agents import Agent
10
+ from agent_framework._middleware import MiddlewareTypes
11
+ from agent_framework._sessions import BaseContextProvider
12
+ from agent_framework._settings import SecretString, load_settings
13
+ from agent_framework._tools import FunctionTool, ToolTypes, normalize_tools
14
+ from openai import AsyncOpenAI
15
+ from openai.types.beta.assistant import Assistant
16
+ from pydantic import BaseModel
17
+
18
+ from ._assistants_client import OpenAIAssistantsClient # type: ignore[reportDeprecated]
19
+ from ._shared import OpenAISettings, from_assistant_tools, to_assistant_tools
20
+
21
+ if TYPE_CHECKING:
22
+ from ._assistants_client import OpenAIAssistantsOptions
23
+
24
+ if sys.version_info >= (3, 13):
25
+ from typing import TypeVar # type:ignore # pragma: no cover
26
+ else:
27
+ from typing_extensions import TypeVar # type:ignore # pragma: no cover
28
+ if sys.version_info >= (3, 11):
29
+ from typing import Self, TypedDict # type:ignore # pragma: no cover
30
+ else:
31
+ from typing_extensions import Self, TypedDict # type:ignore # pragma: no cover
32
+
33
+
34
+ # Type variable for options - allows typed OpenAIAssistantProvider[OptionsCoT] returns
35
+ # Default matches OpenAIAssistantsClient's default options type
36
+ OptionsCoT = TypeVar(
37
+ "OptionsCoT",
38
+ bound=TypedDict, # type: ignore[valid-type]
39
+ default="OpenAIAssistantsOptions",
40
+ covariant=True,
41
+ )
42
+
43
+
44
+ class OpenAIAssistantProvider(Generic[OptionsCoT]):
45
+ """Provider for creating Agent instances from OpenAI Assistants API.
46
+
47
+ This provider allows you to create, retrieve, and wrap OpenAI Assistants
48
+ as Agent instances for use in the agent framework.
49
+
50
+ Examples:
51
+ Basic usage with automatic client creation:
52
+
53
+ .. code-block:: python
54
+
55
+ from agent_framework.openai import OpenAIAssistantProvider
56
+
57
+ # Uses OPENAI_API_KEY environment variable
58
+ provider = OpenAIAssistantProvider()
59
+
60
+ # Create a new assistant
61
+ agent = await provider.create_agent(
62
+ name="MyAssistant",
63
+ model="gpt-4",
64
+ instructions="You are a helpful assistant.",
65
+ tools=[my_function],
66
+ )
67
+
68
+ result = await agent.run("Hello!")
69
+
70
+ Using an existing client:
71
+
72
+ .. code-block:: python
73
+
74
+ from openai import AsyncOpenAI
75
+ from agent_framework.openai import OpenAIAssistantProvider
76
+
77
+ client = AsyncOpenAI()
78
+ provider = OpenAIAssistantProvider(client)
79
+
80
+ # Get an existing assistant by ID
81
+ agent = await provider.get_agent(
82
+ assistant_id="asst_123",
83
+ tools=[my_function], # Provide implementations for function tools
84
+ )
85
+
86
+ Wrapping an SDK Assistant object:
87
+
88
+ .. code-block:: python
89
+
90
+ # Fetch assistant directly via SDK
91
+ assistant = await client.beta.assistants.retrieve("asst_123")
92
+
93
+ # Wrap without additional HTTP call
94
+ agent = provider.as_agent(assistant, tools=[my_function])
95
+ """
96
+
97
+ def __init__(
98
+ self,
99
+ client: AsyncOpenAI | None = None,
100
+ *,
101
+ api_key: str | SecretString | Callable[[], str | Awaitable[str]] | None = None,
102
+ org_id: str | None = None,
103
+ base_url: str | None = None,
104
+ env_file_path: str | None = None,
105
+ env_file_encoding: str | None = None,
106
+ ) -> None:
107
+ """Initialize the OpenAI Assistant Provider.
108
+
109
+ Args:
110
+ client: An existing AsyncOpenAI client to use. If not provided,
111
+ a new client will be created using the other parameters.
112
+
113
+ Keyword Args:
114
+ api_key: OpenAI API key. Can also be set via OPENAI_API_KEY env var.
115
+ org_id: OpenAI organization ID. Can also be set via OPENAI_ORG_ID env var.
116
+ base_url: Base URL for the OpenAI API. Can also be set via OPENAI_BASE_URL env var.
117
+ env_file_path: Path to .env file for configuration.
118
+ env_file_encoding: Encoding of the .env file.
119
+
120
+ Raises:
121
+ ValueError: If no client is provided and API key is missing.
122
+
123
+ Examples:
124
+ .. code-block:: python
125
+
126
+ # Using environment variables
127
+ provider = OpenAIAssistantProvider()
128
+
129
+ # Using explicit API key
130
+ provider = OpenAIAssistantProvider(api_key="sk-...")
131
+
132
+ # Using existing client
133
+ client = AsyncOpenAI()
134
+ provider = OpenAIAssistantProvider(client)
135
+ """
136
+ self._client: AsyncOpenAI | None = client
137
+ self._should_close_client: bool = client is None
138
+
139
+ if client is None:
140
+ # Load settings and create client
141
+ settings = load_settings(
142
+ OpenAISettings,
143
+ env_prefix="OPENAI_",
144
+ api_key=api_key,
145
+ org_id=org_id,
146
+ base_url=base_url,
147
+ env_file_path=env_file_path,
148
+ env_file_encoding=env_file_encoding,
149
+ )
150
+
151
+ api_key_setting = settings.get("api_key")
152
+ if not api_key_setting:
153
+ raise ValueError(
154
+ "OpenAI API key is required. Set via 'api_key' parameter or 'OPENAI_API_KEY' environment variable."
155
+ )
156
+
157
+ # Get API key value
158
+ api_key_value: str | Callable[[], str | Awaitable[str]]
159
+ if isinstance(api_key_setting, SecretString):
160
+ api_key_value = api_key_setting.get_secret_value()
161
+ else:
162
+ api_key_value = api_key_setting
163
+
164
+ # Create client
165
+ client_args: dict[str, Any] = {"api_key": api_key_value}
166
+ if org_id_value := settings.get("org_id"):
167
+ client_args["organization"] = org_id_value
168
+ if base_url_value := settings.get("base_url"):
169
+ client_args["base_url"] = base_url_value
170
+
171
+ self._client = AsyncOpenAI(**client_args)
172
+
173
+ async def __aenter__(self) -> Self:
174
+ """Async context manager entry."""
175
+ return self
176
+
177
+ async def __aexit__(self, exc_type: type[BaseException] | None, exc_val: BaseException | None, exc_tb: Any) -> None:
178
+ """Async context manager exit."""
179
+ await self.close()
180
+
181
+ async def close(self) -> None:
182
+ """Close the provider and clean up resources.
183
+
184
+ If the provider created its own client, it will be closed.
185
+ If an external client was provided, it will not be closed.
186
+ """
187
+ if self._should_close_client and self._client is not None:
188
+ await self._client.close()
189
+
190
+ async def create_agent(
191
+ self,
192
+ *,
193
+ name: str,
194
+ model: str,
195
+ instructions: str | None = None,
196
+ description: str | None = None,
197
+ tools: ToolTypes | Callable[..., Any] | Sequence[ToolTypes | Callable[..., Any]] | None = None,
198
+ metadata: dict[str, str] | None = None,
199
+ default_options: OptionsCoT | None = None,
200
+ middleware: Sequence[MiddlewareTypes] | None = None,
201
+ context_providers: Sequence[BaseContextProvider] | None = None,
202
+ ) -> Agent[OptionsCoT]:
203
+ """Create a new assistant on OpenAI and return a Agent.
204
+
205
+ This method creates a new assistant on the OpenAI service and wraps it
206
+ in a Agent instance. The assistant will persist on OpenAI until deleted.
207
+
208
+ Keyword Args:
209
+ name: The name of the assistant (required).
210
+ model: The model ID to use, e.g., "gpt-4", "gpt-4o" (required).
211
+ instructions: System instructions for the assistant.
212
+ description: A description of the assistant.
213
+ tools: Tools available to the assistant. Can include:
214
+ - FunctionTool instances or callables decorated with @tool
215
+ - Dict-based tools from OpenAIAssistantsClient.get_code_interpreter_tool()
216
+ - Dict-based tools from OpenAIAssistantsClient.get_file_search_tool()
217
+ - Raw tool dictionaries
218
+ metadata: Metadata to attach to the assistant (max 16 key-value pairs).
219
+ default_options: A TypedDict containing default chat options for the agent.
220
+ These options are applied to every run unless overridden.
221
+ Include ``response_format`` here for structured output responses.
222
+ middleware: MiddlewareTypes for the Agent.
223
+ context_providers: Context providers for the Agent.
224
+
225
+ Returns:
226
+ A Agent instance wrapping the created assistant.
227
+
228
+ Raises:
229
+ ValueError: If assistant creation fails.
230
+
231
+ Examples:
232
+ .. code-block:: python
233
+
234
+ provider = OpenAIAssistantProvider()
235
+
236
+ # Create with function tools
237
+ agent = await provider.create_agent(
238
+ name="WeatherBot",
239
+ model="gpt-4",
240
+ instructions="You are a helpful weather assistant.",
241
+ tools=[get_weather],
242
+ )
243
+
244
+ # Create with structured output
245
+ agent = await provider.create_agent(
246
+ name="StructuredBot",
247
+ model="gpt-4",
248
+ default_options={"response_format": MyPydanticModel},
249
+ )
250
+ """
251
+ # Normalize tools
252
+ normalized_tools = normalize_tools(tools)
253
+ assistant_tools: list[FunctionTool | MutableMapping[str, Any]] = [
254
+ tool for tool in normalized_tools if isinstance(tool, (FunctionTool, MutableMapping))
255
+ ]
256
+ api_tools = to_assistant_tools(assistant_tools) if assistant_tools else []
257
+
258
+ # Extract response_format from default_options if present
259
+ opts = dict(default_options) if default_options else {}
260
+ response_format = opts.get("response_format")
261
+
262
+ # Build assistant creation parameters
263
+ create_params: dict[str, Any] = {
264
+ "model": model,
265
+ "name": name,
266
+ }
267
+
268
+ if instructions is not None:
269
+ create_params["instructions"] = instructions
270
+ if description is not None:
271
+ create_params["description"] = description
272
+ if api_tools:
273
+ create_params["tools"] = api_tools
274
+ if metadata is not None:
275
+ create_params["metadata"] = metadata
276
+
277
+ # Handle response format for OpenAI API
278
+ if response_format is not None and isinstance(response_format, type) and issubclass(response_format, BaseModel):
279
+ create_params["response_format"] = {
280
+ "type": "json_schema",
281
+ "json_schema": {
282
+ "name": response_format.__name__,
283
+ "schema": response_format.model_json_schema(),
284
+ "strict": True,
285
+ },
286
+ }
287
+
288
+ # Create the assistant
289
+ if not self._client:
290
+ raise RuntimeError("OpenAI client is not initialized.")
291
+
292
+ assistant = await self._client.beta.assistants.create(**create_params) # type: ignore[reportDeprecated]
293
+
294
+ # Create Agent - pass default_options which contains response_format
295
+ return self._create_chat_agent_from_assistant(
296
+ assistant=assistant,
297
+ tools=normalized_tools,
298
+ instructions=instructions,
299
+ middleware=middleware,
300
+ context_providers=context_providers,
301
+ default_options=default_options,
302
+ )
303
+
304
+ async def get_agent(
305
+ self,
306
+ assistant_id: str,
307
+ *,
308
+ tools: ToolTypes | Callable[..., Any] | Sequence[ToolTypes | Callable[..., Any]] | None = None,
309
+ instructions: str | None = None,
310
+ default_options: OptionsCoT | None = None,
311
+ middleware: Sequence[MiddlewareTypes] | None = None,
312
+ context_providers: Sequence[BaseContextProvider] | None = None,
313
+ ) -> Agent[OptionsCoT]:
314
+ """Retrieve an existing assistant by ID and return a Agent.
315
+
316
+ This method fetches an existing assistant from OpenAI by its ID
317
+ and wraps it in a Agent instance.
318
+
319
+ Args:
320
+ assistant_id: The ID of the assistant to retrieve (e.g., "asst_123").
321
+
322
+ Keyword Args:
323
+ tools: Function tools to make available. IMPORTANT: If the assistant
324
+ was created with function tools, you MUST provide matching
325
+ implementations here. Hosted tools (code_interpreter, file_search)
326
+ are automatically included.
327
+ instructions: Override the assistant's instructions (optional).
328
+ default_options: A TypedDict containing default chat options for the agent.
329
+ These options are applied to every run unless overridden.
330
+ middleware: MiddlewareTypes for the Agent.
331
+ context_providers: Context providers for the Agent.
332
+
333
+ Returns:
334
+ A Agent instance wrapping the retrieved assistant.
335
+
336
+ Raises:
337
+ RuntimeError: If the assistant cannot be retrieved.
338
+ ValueError: If required function tools are missing.
339
+
340
+ Examples:
341
+ .. code-block:: python
342
+
343
+ provider = OpenAIAssistantProvider()
344
+
345
+ # Get assistant without function tools
346
+ agent = await provider.get_agent(assistant_id="asst_123")
347
+
348
+ # Get assistant with function tools
349
+ agent = await provider.get_agent(
350
+ assistant_id="asst_456",
351
+ tools=[get_weather, search_database], # Implementations required!
352
+ )
353
+ """
354
+ # Fetch the assistant
355
+ if not self._client:
356
+ raise RuntimeError("OpenAI client is not initialized.")
357
+
358
+ assistant = await self._client.beta.assistants.retrieve(assistant_id) # type: ignore[reportDeprecated]
359
+
360
+ # Use as_agent to wrap it
361
+ return self.as_agent(
362
+ assistant=assistant,
363
+ tools=tools,
364
+ instructions=instructions,
365
+ default_options=default_options,
366
+ middleware=middleware,
367
+ context_providers=context_providers,
368
+ )
369
+
370
+ def as_agent(
371
+ self,
372
+ assistant: Assistant,
373
+ *,
374
+ tools: ToolTypes | Callable[..., Any] | Sequence[ToolTypes | Callable[..., Any]] | None = None,
375
+ instructions: str | None = None,
376
+ default_options: OptionsCoT | None = None,
377
+ middleware: Sequence[MiddlewareTypes] | None = None,
378
+ context_providers: Sequence[BaseContextProvider] | None = None,
379
+ ) -> Agent[OptionsCoT]:
380
+ """Wrap an existing SDK Assistant object as a Agent.
381
+
382
+ This method does NOT make any HTTP calls. It simply wraps an already-
383
+ fetched Assistant object in a Agent.
384
+
385
+ Args:
386
+ assistant: The OpenAI Assistant SDK object to wrap.
387
+
388
+ Keyword Args:
389
+ tools: Function tools to make available. If the assistant has
390
+ function tools defined, you MUST provide matching implementations.
391
+ Hosted tools (code_interpreter, file_search) are automatically included.
392
+ instructions: Override the assistant's instructions (optional).
393
+ default_options: A TypedDict containing default chat options for the agent.
394
+ These options are applied to every run unless overridden.
395
+ middleware: MiddlewareTypes for the Agent.
396
+ context_providers: Context providers for the Agent.
397
+
398
+ Returns:
399
+ A Agent instance wrapping the assistant.
400
+
401
+ Raises:
402
+ ValueError: If required function tools are missing.
403
+
404
+ Examples:
405
+ .. code-block:: python
406
+
407
+ client = AsyncOpenAI()
408
+ provider = OpenAIAssistantProvider(client)
409
+
410
+ # Fetch assistant via SDK
411
+ assistant = await client.beta.assistants.retrieve("asst_123")
412
+
413
+ # Wrap without additional HTTP call
414
+ agent = provider.as_agent(
415
+ assistant,
416
+ tools=[my_function],
417
+ instructions="Custom instructions override",
418
+ )
419
+ """
420
+ # Validate that required function tools are provided
421
+ self._validate_function_tools(assistant.tools or [], tools)
422
+
423
+ # Merge hosted tools with user-provided function tools
424
+ merged_tools = self._merge_tools(assistant.tools or [], tools)
425
+
426
+ # Create Agent
427
+ return self._create_chat_agent_from_assistant(
428
+ assistant=assistant,
429
+ tools=merged_tools,
430
+ instructions=instructions,
431
+ default_options=default_options,
432
+ middleware=middleware,
433
+ context_providers=context_providers,
434
+ )
435
+
436
+ def _validate_function_tools(
437
+ self,
438
+ assistant_tools: list[Any],
439
+ provided_tools: ToolTypes | Callable[..., Any] | Sequence[ToolTypes | Callable[..., Any]] | None,
440
+ ) -> None:
441
+ """Validate that required function tools are provided.
442
+
443
+ Args:
444
+ assistant_tools: Tools defined on the assistant.
445
+ provided_tools: Tools provided by the user.
446
+
447
+ Raises:
448
+ ValueError: If a required function tool is missing.
449
+ """
450
+ # Get function tool names from assistant
451
+ required_functions: set[str] = set()
452
+ for tool in assistant_tools:
453
+ if (
454
+ hasattr(tool, "type")
455
+ and tool.type == "function"
456
+ and hasattr(tool, "function")
457
+ and hasattr(tool.function, "name")
458
+ ):
459
+ required_functions.add(tool.function.name)
460
+
461
+ if not required_functions:
462
+ return # No function tools required
463
+
464
+ # Get provided function names using normalize_tools
465
+ provided_functions: set[str] = set()
466
+ if provided_tools is not None:
467
+ normalized = normalize_tools(provided_tools)
468
+ for tool in normalized:
469
+ if isinstance(tool, FunctionTool):
470
+ provided_functions.add(tool.name)
471
+ elif isinstance(tool, Mapping):
472
+ typed_tool = cast(Mapping[str, Any], tool)
473
+ raw_func_spec = typed_tool.get("function")
474
+ if isinstance(raw_func_spec, Mapping):
475
+ typed_func_spec = cast(Mapping[str, Any], raw_func_spec)
476
+ raw_name = typed_func_spec.get("name")
477
+ if isinstance(raw_name, str) and raw_name:
478
+ provided_functions.add(raw_name)
479
+
480
+ # Check for missing functions
481
+ missing = required_functions - provided_functions
482
+ if missing:
483
+ missing_list = ", ".join(sorted(missing))
484
+ raise ValueError(
485
+ f"Assistant requires function tool(s) '{missing_list}' but no implementation was provided. "
486
+ f"Please pass the function implementation(s) in the 'tools' parameter."
487
+ )
488
+
489
+ def _merge_tools(
490
+ self,
491
+ assistant_tools: list[Any],
492
+ user_tools: ToolTypes | Callable[..., Any] | Sequence[ToolTypes | Callable[..., Any]] | None,
493
+ ) -> list[FunctionTool | MutableMapping[str, Any] | Any]:
494
+ """Merge hosted tools from assistant with user-provided function tools.
495
+
496
+ Args:
497
+ assistant_tools: Tools defined on the assistant.
498
+ user_tools: Tools provided by the user.
499
+
500
+ Returns:
501
+ A list of all tools (hosted tools + user function implementations).
502
+ """
503
+ merged: list[FunctionTool | MutableMapping[str, Any] | Any] = []
504
+
505
+ # Add hosted tools from assistant using shared conversion
506
+ hosted_tools = from_assistant_tools(assistant_tools)
507
+ merged.extend(hosted_tools)
508
+
509
+ # Add user-provided tools (normalized)
510
+ if user_tools is not None:
511
+ normalized_user_tools = normalize_tools(user_tools)
512
+ merged.extend(normalized_user_tools)
513
+
514
+ return merged
515
+
516
+ def _create_chat_agent_from_assistant(
517
+ self,
518
+ assistant: Assistant,
519
+ tools: list[FunctionTool | MutableMapping[str, Any] | Any] | None,
520
+ instructions: str | None,
521
+ middleware: Sequence[MiddlewareTypes] | None,
522
+ context_providers: Sequence[BaseContextProvider] | None,
523
+ default_options: OptionsCoT | None = None,
524
+ **kwargs: Any,
525
+ ) -> Agent[OptionsCoT]:
526
+ """Create a Agent from an Assistant.
527
+
528
+ Args:
529
+ assistant: The OpenAI Assistant object.
530
+ tools: Tools for the agent.
531
+ instructions: Instructions override.
532
+ middleware: MiddlewareTypes for the agent.
533
+ context_providers: Context providers for the agent.
534
+ default_options: Default chat options for the agent (may include response_format).
535
+ **kwargs: Additional arguments passed to Agent.
536
+
537
+ Returns:
538
+ A configured Agent instance.
539
+ """
540
+ # Create the chat client with the assistant
541
+ client = OpenAIAssistantsClient( # type: ignore[reportDeprecated]
542
+ model=assistant.model,
543
+ assistant_id=assistant.id,
544
+ assistant_name=assistant.name,
545
+ assistant_description=assistant.description,
546
+ async_client=self._client,
547
+ )
548
+
549
+ # Use instructions from assistant if not overridden
550
+ final_instructions = instructions if instructions is not None else assistant.instructions
551
+
552
+ # Create and return Agent
553
+ return Agent(
554
+ client=client,
555
+ id=assistant.id,
556
+ name=assistant.name,
557
+ description=assistant.description,
558
+ instructions=final_instructions,
559
+ tools=tools if tools else None,
560
+ middleware=middleware,
561
+ context_providers=context_providers,
562
+ default_options=default_options, # type: ignore[arg-type]
563
+ **kwargs,
564
+ )