Skip to content

## Custom Model Provider Not Working #485

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
Sghosh1999 opened this issue Apr 11, 2025 · 3 comments
Open

## Custom Model Provider Not Working #485

Sghosh1999 opened this issue Apr 11, 2025 · 3 comments
Labels
bug Something isn't working needs-more-info Waiting for a reply/more info from the author

Comments

@Sghosh1999
Copy link


📌 Context

I'm currently working on integrating a custom LLM into my application. Specifically, I'm using Groq's llama3-8b-8192 model through the ChatOpenAI class from the langchain_openai package:

from langchain_openai import ChatOpenAI

llm = ChatOpenAI(
    openai_api_base="https://api.groq.com/openai/v1",
    openai_api_key="gsk_XXXXXXXXXXXXXXXXXXXXXXXXXXXX",
    model_name="llama3-8b-8192",
    temperature=0,
    max_tokens=1000,
)

I aim to integrate this model into my existing setup, which utilizes a custom ModelProvider:

from openai import AsyncOpenAI
from agents import ModelProvider, Model, OpenAIChatCompletionsModel

client = AsyncOpenAI(base_url='', api_key='')

class CustomModelProvider(ModelProvider):
    def get_model(self, model_name: str | None) -> Model:
        return ChatOpenAI(
                      openai_api_base="https://api.groq.com/openai/v1",
                      openai_api_key="gsk_XXXXXXXXXXXXXXXXXXXXXXXXXXXX",
                      model_name="llama3-8b-8192",
                      temperature=0,
                      max_tokens=1000,
                  )

CUSTOM_MODEL_PROVIDER = CustomModelProvider()

Please guide how to do it.

@Sghosh1999 Sghosh1999 added the bug Something isn't working label Apr 11, 2025
@rm-openai
Copy link
Collaborator

What errors are you running into?

@rm-openai rm-openai added the needs-more-info Waiting for a reply/more info from the author label Apr 14, 2025
Copy link

This issue is stale because it has been open for 7 days with no activity.

@github-actions github-actions bot added the stale label Apr 22, 2025
@jlchereau
Copy link

I have a resembling issue which might help:

from openai import AsyncOpenAI
from agents import OpenAIChatCompletionsModel
client = AsyncOpenAI(
    base_url="https://generativelanguage.googleapis.com/v1beta/openai/",
    api_key=os.getenv("GOOGLE_API_KEY")
)
# Try Both - same error
# model=OpenAIChatCompletionsModel(model="gemini/gemini-2.0-flash-lite", openai_client=client)
model=OpenAIChatCompletionsModel(model="gpt-4o", openai_client=AsyncOpenAI())

Then:

from agents import Agent, Runner

agent = Agent(
    name="Assistant",
    instructions="You are a poetic assistant",
    model=model
)

result = await Runner.run(agent, "Write a haiku about recursion in programming.")
print(result.final_output)

Raises:

2025-04-24 18:47:52,869 - ERROR - Error getting response: Object of type OpenAIChatCompletionsModel is not JSON serializable. (request_id: None)
---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
Cell In[70], line 9
      1 from agents import Agent, Runner
      3 agent = Agent(
      4     name="Assistant",
      5     instructions="You are a poetic assistant",
      6     model=model
      7 )
----> 9 result = await Runner.run(agent, "Write a haiku about recursion in programming.")
     10 print(result.final_output)
     12 # Code within the code,
     13 # Functions calling themselves,
     14 # Infinite loop's dance.

File ~/my_project/.venv/lib/python3.12/site-packages/agents/run.py:218, in Runner.run(cls, starting_agent, input, context, max_turns, hooks, run_config, previous_response_id)
    213 logger.debug(
    214     f"Running agent {current_agent.name} (turn {current_turn})",
    215 )
    217 if current_turn == 1:
--> 218     input_guardrail_results, turn_result = await asyncio.gather(
    219         cls._run_input_guardrails(
    220             starting_agent,
    221             starting_agent.input_guardrails
    222             + (run_config.input_guardrails or []),
    223             copy.deepcopy(input),
    224             context_wrapper,
    225         ),
    226         cls._run_single_turn(
    227             agent=current_agent,
    228             all_tools=all_tools,
    229             original_input=original_input,
    230             generated_items=generated_items,
    231             hooks=hooks,
    232             context_wrapper=context_wrapper,
    233             run_config=run_config,
    234             should_run_agent_start_hooks=should_run_agent_start_hooks,
    235             tool_use_tracker=tool_use_tracker,
    236             previous_response_id=previous_response_id,
    237         ),
    238     )
    239 else:
    240     turn_result = await cls._run_single_turn(
    241         agent=current_agent,
    242         all_tools=all_tools,
   (...)    250         previous_response_id=previous_response_id,
    251     )

File /Library/Frameworks/Python.framework/Versions/3.12/lib/python3.12/asyncio/tasks.py:385, in Task.__wakeup(self, future)
    383 def __wakeup(self, future):
    384     try:
--> 385         future.result()
    386     except BaseException as exc:
    387         # This may also be a cancellation.
    388         self.__step(exc)

File /Library/Frameworks/Python.framework/Versions/3.12/lib/python3.12/asyncio/tasks.py:314, in Task.__step_run_and_handle_result(***failed resolving arguments***)
    310 try:
    311     if exc is None:
    312         # We use the `send` method directly, because coroutines
    313         # don't have `__iter__` and `__next__` methods.
--> 314         result = coro.send(None)
    315     else:
    316         result = coro.throw(exc)

File ~/my_project/.venv/lib/python3.12/site-packages/agents/run.py:757, in Runner._run_single_turn(cls, agent, all_tools, original_input, generated_items, hooks, context_wrapper, run_config, should_run_agent_start_hooks, tool_use_tracker, previous_response_id)
    754 input = ItemHelpers.input_to_new_input_list(original_input)
    755 input.extend([generated_item.to_input_item() for generated_item in generated_items])
--> 757 new_response = await cls._get_new_response(
    758     agent,
    759     system_prompt,
    760     input,
    761     output_schema,
    762     all_tools,
    763     handoffs,
    764     context_wrapper,
    765     run_config,
    766     tool_use_tracker,
    767     previous_response_id,
    768 )
    770 return await cls._get_single_step_result_from_response(
    771     agent=agent,
    772     original_input=original_input,
   (...)    781     tool_use_tracker=tool_use_tracker,
    782 )

File ~/my_project/.venv/lib/python3.12/site-packages/agents/run.py:916, in Runner._get_new_response(cls, agent, system_prompt, input, output_schema, all_tools, handoffs, context_wrapper, run_config, tool_use_tracker, previous_response_id)
    913 model_settings = agent.model_settings.resolve(run_config.model_settings)
    914 model_settings = RunImpl.maybe_reset_tool_choice(agent, tool_use_tracker, model_settings)
--> 916 new_response = await model.get_response(
    917     system_instructions=system_prompt,
    918     input=input,
    919     model_settings=model_settings,
    920     tools=all_tools,
    921     output_schema=output_schema,
    922     handoffs=handoffs,
    923     tracing=get_model_tracing_impl(
    924         run_config.tracing_disabled, run_config.trace_include_sensitive_data
    925     ),
    926     previous_response_id=previous_response_id,
    927 )
    929 context_wrapper.usage.add(new_response.usage)
    931 return new_response

File ~/my_project/.venv/lib/python3.12/site-packages/agents/models/openai_responses.py:76, in OpenAIResponsesModel.get_response(self, system_instructions, input, model_settings, tools, output_schema, handoffs, tracing, previous_response_id)
     74 with response_span(disabled=tracing.is_disabled()) as span_response:
     75     try:
---> 76         response = await self._fetch_response(
     77             system_instructions,
     78             input,
     79             model_settings,
     80             tools,
     81             output_schema,
     82             handoffs,
     83             previous_response_id,
     84             stream=False,
     85         )
     87         if _debug.DONT_LOG_MODEL_DATA:
     88             logger.debug("LLM responded")

File ~/my_project/.venv/lib/python3.12/site-packages/agents/models/openai_responses.py:242, in OpenAIResponsesModel._fetch_response(self, system_instructions, input, model_settings, tools, output_schema, handoffs, previous_response_id, stream)
    231 else:
    232     logger.debug(
    233         f"Calling LLM {self.model} with input:\n"
    234         f"{json.dumps(list_input, indent=2)}\n"
   (...)    239         f"Previous response id: {previous_response_id}\n"
    240     )
--> 242 return await self._client.responses.create(
    243     previous_response_id=self._non_null_or_not_given(previous_response_id),
    244     instructions=self._non_null_or_not_given(system_instructions),
    245     model=self.model,
    246     input=list_input,
    247     include=converted_tools.includes,
    248     tools=converted_tools.tools,
    249     temperature=self._non_null_or_not_given(model_settings.temperature),
    250     top_p=self._non_null_or_not_given(model_settings.top_p),
    251     truncation=self._non_null_or_not_given(model_settings.truncation),
    252     max_output_tokens=self._non_null_or_not_given(model_settings.max_tokens),
    253     tool_choice=tool_choice,
    254     parallel_tool_calls=parallel_tool_calls,
    255     stream=stream,
    256     extra_headers=_HEADERS,
    257     extra_query=model_settings.extra_query,
    258     extra_body=model_settings.extra_body,
    259     text=response_format,
    260     store=self._non_null_or_not_given(model_settings.store),
    261     reasoning=self._non_null_or_not_given(model_settings.reasoning),
    262     metadata=self._non_null_or_not_given(model_settings.metadata),
    263 )

File ~/my_project/.venv/lib/python3.12/site-packages/openai/resources/responses/responses.py:1534, in AsyncResponses.create(self, input, model, include, instructions, max_output_tokens, metadata, parallel_tool_calls, previous_response_id, reasoning, service_tier, store, stream, temperature, text, tool_choice, tools, top_p, truncation, user, extra_headers, extra_query, extra_body, timeout)
   1504 @required_args(["input", "model"], ["input", "model", "stream"])
   1505 async def create(
   1506     self,
   (...)   1532     timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN,
   1533 ) -> Response | AsyncStream[ResponseStreamEvent]:
-> 1534     return await self._post(
   1535         "/responses",
   1536         body=await async_maybe_transform(
   1537             {
   1538                 "input": input,
   1539                 "model": model,
   1540                 "include": include,
   1541                 "instructions": instructions,
   1542                 "max_output_tokens": max_output_tokens,
   1543                 "metadata": metadata,
   1544                 "parallel_tool_calls": parallel_tool_calls,
   1545                 "previous_response_id": previous_response_id,
   1546                 "reasoning": reasoning,
   1547                 "service_tier": service_tier,
   1548                 "store": store,
   1549                 "stream": stream,
   1550                 "temperature": temperature,
   1551                 "text": text,
   1552                 "tool_choice": tool_choice,
   1553                 "tools": tools,
   1554                 "top_p": top_p,
   1555                 "truncation": truncation,
   1556                 "user": user,
   1557             },
   1558             response_create_params.ResponseCreateParamsStreaming
   1559             if stream
   1560             else response_create_params.ResponseCreateParamsNonStreaming,
   1561         ),
   1562         options=make_request_options(
   1563             extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
   1564         ),
   1565         cast_to=Response,
   1566         stream=stream or False,
   1567         stream_cls=AsyncStream[ResponseStreamEvent],
   1568     )

File ~/my_project/.venv/lib/python3.12/site-packages/openai/_base_client.py:1805, in AsyncAPIClient.post(self, path, cast_to, body, files, options, stream, stream_cls)
   1791 async def post(
   1792     self,
   1793     path: str,
   (...)   1800     stream_cls: type[_AsyncStreamT] | None = None,
   1801 ) -> ResponseT | _AsyncStreamT:
   1802     opts = FinalRequestOptions.construct(
   1803         method="post", url=path, json_data=body, files=await async_to_httpx_files(files), **options
   1804     )
-> 1805     return await self.request(cast_to, opts, stream=stream, stream_cls=stream_cls)

File ~/my_project/.venv/lib/python3.12/site-packages/openai/_base_client.py:1495, in AsyncAPIClient.request(self, cast_to, options, stream, stream_cls, remaining_retries)
   1492 else:
   1493     retries_taken = 0
-> 1495 return await self._request(
   1496     cast_to=cast_to,
   1497     options=options,
   1498     stream=stream,
   1499     stream_cls=stream_cls,
   1500     retries_taken=retries_taken,
   1501 )

File ~/my_project/.venv/lib/python3.12/site-packages/openai/_base_client.py:1526, in AsyncAPIClient._request(self, cast_to, options, stream, stream_cls, retries_taken)
   1523 options = await self._prepare_options(options)
   1525 remaining_retries = options.get_max_retries(self.max_retries) - retries_taken
-> 1526 request = self._build_request(options, retries_taken=retries_taken)
   1527 await self._prepare_request(request)
   1529 if options.idempotency_key:
   1530     # ensure the idempotency key is reused between requests

File ~/my_project/.venv/lib/python3.12/site-packages/openai/_base_client.py:536, in BaseClient._build_request(self, options, retries_taken)
    533     kwargs["extensions"] = {"sni_hostname": prepared_url.host.replace("_", "-")}
    535 # TODO: report this error to httpx
--> 536 return self._client.build_request(  # pyright: ignore[reportUnknownMemberType]
    537     headers=headers,
    538     timeout=self.timeout if isinstance(options.timeout, NotGiven) else options.timeout,
    539     method=options.method,
    540     url=prepared_url,
    541     # the `Query` type that we use is incompatible with qs'
    542     # `Params` type as it needs to be typed as `Mapping[str, object]`
    543     # so that passing a `TypedDict` doesn't cause an error.
    544     # https://github.com/microsoft/pyright/issues/3526#event-6715453066
    545     params=self.qs.stringify(cast(Mapping[str, Any], params)) if params else None,
    546     json=json_data if is_given(json_data) else None,
    547     files=files,
    548     **kwargs,
    549 )

File ~/my_project/.venv/lib/python3.12/site-packages/httpx/_client.py:378, in BaseClient.build_request(self, method, url, content, data, files, json, params, headers, cookies, timeout, extensions)
    372     timeout = (
    373         self.timeout
    374         if isinstance(timeout, UseClientDefault)
    375         else Timeout(timeout)
    376     )
    377     extensions = dict(**extensions, timeout=timeout.as_dict())
--> 378 return Request(
    379     method,
    380     url,
    381     content=content,
    382     data=data,
    383     files=files,
    384     json=json,
    385     params=params,
    386     headers=headers,
    387     cookies=cookies,
    388     extensions=extensions,
    389 )

File ~/my_project/.venv/lib/python3.12/site-packages/httpx/_models.py:408, in Request.__init__(self, method, url, params, headers, cookies, content, data, files, json, stream, extensions)
    406 if stream is None:
    407     content_type: str | None = self.headers.get("content-type")
--> 408     headers, stream = encode_request(
    409         content=content,
    410         data=data,
    411         files=files,
    412         json=json,
    413         boundary=get_multipart_boundary_from_content_type(
    414             content_type=content_type.encode(self.headers.encoding)
    415             if content_type
    416             else None
    417         ),
    418     )
    419     self._prepare(headers)
    420     self.stream = stream

File ~/my_project/.venv/lib/python3.12/site-packages/httpx/_content.py:216, in encode_request(content, data, files, json, boundary)
    214     return encode_urlencoded_data(data)
    215 elif json is not None:
--> 216     return encode_json(json)
    218 return {}, ByteStream(b"")

File ~/my_project/.venv/lib/python3.12/site-packages/httpx/_content.py:177, in encode_json(json)
    176 def encode_json(json: Any) -> tuple[dict[str, str], ByteStream]:
--> 177     body = json_dumps(
    178         json, ensure_ascii=False, separators=(",", ":"), allow_nan=False
    179     ).encode("utf-8")
    180     content_length = str(len(body))
    181     content_type = "application/json"

File /Library/Frameworks/Python.framework/Versions/3.12/lib/python3.12/json/__init__.py:238, in dumps(obj, skipkeys, ensure_ascii, check_circular, allow_nan, cls, indent, separators, default, sort_keys, **kw)
    232 if cls is None:
    233     cls = JSONEncoder
    234 return cls(
    235     skipkeys=skipkeys, ensure_ascii=ensure_ascii,
    236     check_circular=check_circular, allow_nan=allow_nan, indent=indent,
    237     separators=separators, default=default, sort_keys=sort_keys,
--> 238     **kw).encode(obj)

File /Library/Frameworks/Python.framework/Versions/3.12/lib/python3.12/json/encoder.py:200, in JSONEncoder.encode(self, o)
    196         return encode_basestring(o)
    197 # This doesn't pass the iterator directly to ''.join() because the
    198 # exceptions aren't as detailed.  The list call should be roughly
    199 # equivalent to the PySequence_Fast that ''.join() would do.
--> 200 chunks = self.iterencode(o, _one_shot=True)
    201 if not isinstance(chunks, (list, tuple)):
    202     chunks = list(chunks)

File /Library/Frameworks/Python.framework/Versions/3.12/lib/python3.12/json/encoder.py:258, in JSONEncoder.iterencode(self, o, _one_shot)
    253 else:
    254     _iterencode = _make_iterencode(
    255         markers, self.default, _encoder, self.indent, floatstr,
    256         self.key_separator, self.item_separator, self.sort_keys,
    257         self.skipkeys, _one_shot)
--> 258 return _iterencode(o, 0)

File /Library/Frameworks/Python.framework/Versions/3.12/lib/python3.12/json/encoder.py:180, in JSONEncoder.default(self, o)
    161 def default(self, o):
    162     """Implement this method in a subclass such that it returns
    163     a serializable object for ``o``, or calls the base implementation
    164     (to raise a ``TypeError``).
   (...)    178 
    179     """
--> 180     raise TypeError(f'Object of type {o.__class__.__name__} '
    181                     f'is not JSON serializable')

TypeError: Object of type OpenAIChatCompletionsModel is not JSON serializable

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug Something isn't working needs-more-info Waiting for a reply/more info from the author
Projects
None yet
Development

No branches or pull requests

3 participants