dspy icon indicating copy to clipboard operation
dspy copied to clipboard

[Feature] Cannot use http_client for ssl_context or proxy settings via LiteLLM

Open satyaloka93 opened this issue 8 months ago • 4 comments

What feature would you like to see?

I cannot see any documentation or figure out how to use the dspy.LM adapter with http_client parameter (standard openai) that sets proxy and ssl context settings. This was added to the old adapter here. Is there something I'm missing?

Would you like to contribute?

  • [ ] Yes, I'd like to help implement this.
  • [ ] No, I just want to request it.

Additional Context

No response

satyaloka93 avatar Mar 04 '25 12:03 satyaloka93

Hi @satyaloka93 , is this LiteLLM documentation useful? seems like an external litellm configuration you can set and then just call dspy.LM in the same way as you would.

arnavsinghvi11 avatar Mar 06 '25 18:03 arnavsinghvi11

Ok, when I try setting litellm.client_session = httpx_client, then:

import litellm
import os

response = litellm.completion(
    model="openai/claude-3-5-sonnet-v2",               # add `openai/` prefix to model so litellm knows to route to OpenAI
    api_key="sk-xxxxxx,                  # api key to your openai compatible endpoint
    api_base="http://0.0.0.0:4000",     # set API Base of your Custom OpenAI Endpoint
    messages=[
                {
                    "role": "user",
                    "content": "Hey, how's it going?",
                }
    ],
)
print(response)

It works! However, setting the client_session first then using dspy.LM similarly (model="openai/claude-3-5-sonnet-v2"),, I get:

APIError: litellm.APIError: APIError: OpenAIException - Internal Server Error (litellm_core_utils/exception_mapping_utils.py:404)

Sorry, typing manually from another system, cannot paste entire stack. But it appears simply that dspy.LM does not take the standard required pre-pended provider openai, befor the model, which works using a standard litellm.completion. If you don't prepend 'openai', there is an error about not supplying the provider. I've tried putting provider='openai' as an argument, but that does not work, same error about 'LLM Provider NOT provided'.

satyaloka93 avatar Mar 07 '25 15:03 satyaloka93

Had a chance to look at this @arnavsinghvi11? I would really love to use the new adapter, stuck on old dspy version because of lack of http_client support still.

satyaloka93 avatar Mar 28 '25 13:03 satyaloka93

Hey @satyaloka93 , dspy.LM works with prepending 'openai/' before your model type. Can you check if your httpx_client configuration follows the documentation above and is also compliant with the LiteLLM SSL settings? Setting a proxy server might help as well https://docs.litellm.ai/docs/providers/openai#set-ssl_verifyfalse

arnavsinghvi11 avatar Apr 10 '25 17:04 arnavsinghvi11

I'm in the same situation. I'm having a AWS lambda function proxying an OpenAI compatible LLM. I've written an example class called GenAI_AWSSigV4Transport which basically connects to AWS, authenticates, sends the request to the lambda function and returns the chat completion as returned by the API. This all works fine with litellm but not with dspy.

Any pointers on what to change would be highly appreciated.

>>> import dspy
>>> import litellm
>>> import os
>>> from typing import Literal
>>> litellm.client_session = httpx.Client(transport = GenAI_AWSSigV4Transport(
...     function = settings["GENAI_GATEWAY_FUNCTION"], 
...     role = settings["LAMBDA_ROLE_ARN"]))
>>> response = litellm.completion(
...     model="openai/gpt-4o-mini", 
...     api_key="not-used", 
...     api_base=settings["GENAI_GATEWAY_LAMBDA_VPC_ENDPOINT"],
...     messages=[
...                 {
...                     "role": "user",
...                     "content": "Hey, how's it going?",
...                 }
...     ],
... )
>>> print(response)
ModelResponse(id='chatcmpl-ByPXmUm6SoXEvKlXd8PAjycJvOolx', created=1753737570, model='gpt-4o-mini-2024-07-18', object='chat.completion', system_fingerprint='fp_efad92c60b', choices=[Choices(finish_reason='stop', index=0, message=Message(content="I'm doing well, thanks! How about you?", role='assistant', tool_calls=None, function_call=None, provider_specific_fields={'refusal': None}, annotations=[]), provider_specific_fields={'content_filter_results': {'hate': {'filtered': False, 'severity': 'safe'}, 'self_harm': {'filtered': False, 'severity': 'safe'}, 'sexual': {'filtered': False, 'severity': 'safe'}, 'violence': {'filtered': False, 'severity': 'safe'}}})], usage=Usage(completion_tokens=11, prompt_tokens=14, total_tokens=25, completion_tokens_details=CompletionTokensDetailsWrapper(accepted_prediction_tokens=0, audio_tokens=0, reasoning_tokens=0, rejected_prediction_tokens=0, text_tokens=None), prompt_tokens_details=PromptTokensDetailsWrapper(audio_tokens=0, cached_tokens=0, text_tokens=None, image_tokens=None)), service_tier=None, prompt_filter_results=[{'prompt_index': 0, 'content_filter_results': {'hate': {'filtered': False, 'severity': 'safe'}, 'self_harm': {'filtered': False, 'severity': 'safe'}, 'sexual': {'filtered': False, 'severity': 'safe'}, 'violence': {'filtered': False, 'severity': 'safe'}}}], cache_hit=None)
>>> 
>>> class Classify(dspy.Signature):
...     sentence: str = dspy.InputField()
...     sentiment: Literal["positive", "negative", "neutral"] = dspy.OutputField()
...     confidence: float = dspy.OutputField()
... 
>>> lm = dspy.LM(
...     model = "openai/gpt-4o-mini", 
...     api_key="not-used", 
...     api_base=settings["GENAI_GATEWAY_LAMBDA_VPC_ENDPOINT"], 
...     provider='openai')
>>> dspy.configure(lm=lm)
>>> classify = dspy.Predict(Classify)
>>> classify(sentence="This book was super fun to read, though not the last chapter.")
2025/07/28 23:19:33 WARNING dspy.adapters.json_adapter: Failed to use structured output format, falling back to JSON mode.
Traceback (most recent call last):
  File "/workspace/.pyenv_mirror/user/3.12.3/lib/python3.12/site-packages/litellm/llms/openai/openai.py", line 725, in completion
    raise e
  File "/workspace/.pyenv_mirror/user/3.12.3/lib/python3.12/site-packages/litellm/llms/openai/openai.py", line 653, in completion
    ) = self.make_sync_openai_chat_completion_request(
        ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/workspace/.pyenv_mirror/user/3.12.3/lib/python3.12/site-packages/litellm/litellm_core_utils/logging_utils.py", line 149, in sync_wrapper
    result = func(*args, **kwargs)
             ^^^^^^^^^^^^^^^^^^^^^
  File "/workspace/.pyenv_mirror/user/3.12.3/lib/python3.12/site-packages/litellm/llms/openai/openai.py", line 471, in make_sync_openai_chat_completion_request
    raise e
  File "/workspace/.pyenv_mirror/user/3.12.3/lib/python3.12/site-packages/litellm/llms/openai/openai.py", line 453, in make_sync_openai_chat_completion_request
    raw_response = openai_client.chat.completions.with_raw_response.create(
                   ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/workspace/.pyenv_mirror/user/3.12.3/lib/python3.12/site-packages/openai/_legacy_response.py", line 364, in wrapped
    return cast(LegacyAPIResponse[R], func(*args, **kwargs))
                                      ^^^^^^^^^^^^^^^^^^^^^
  File "/workspace/.pyenv_mirror/user/3.12.3/lib/python3.12/site-packages/openai/_utils/_utils.py", line 287, in wrapper
    return func(*args, **kwargs)
           ^^^^^^^^^^^^^^^^^^^^^
  File "/workspace/.pyenv_mirror/user/3.12.3/lib/python3.12/site-packages/openai/resources/chat/completions/completions.py", line 1087, in create
    return self._post(
           ^^^^^^^^^^^
  File "/workspace/.pyenv_mirror/user/3.12.3/lib/python3.12/site-packages/openai/_base_client.py", line 1256, in post
    return cast(ResponseT, self.request(cast_to, opts, stream=stream, stream_cls=stream_cls))
                           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/workspace/.pyenv_mirror/user/3.12.3/lib/python3.12/site-packages/openai/_base_client.py", line 1044, in request
    raise self._make_status_error_from_response(err.response) from None
openai.PermissionDeniedError: <AccessDeniedException>
  <Message>Unable to determine service/operation name to be authorized</Message>
</AccessDeniedException>

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "/workspace/.pyenv_mirror/user/3.12.3/lib/python3.12/site-packages/litellm/main.py", line 1966, in completion
    raise e
  File "/workspace/.pyenv_mirror/user/3.12.3/lib/python3.12/site-packages/litellm/main.py", line 1939, in completion
    response = openai_chat_completions.completion(
               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/workspace/.pyenv_mirror/user/3.12.3/lib/python3.12/site-packages/litellm/llms/openai/openai.py", line 736, in completion
    raise OpenAIError(
litellm.llms.openai.common_utils.OpenAIError: <AccessDeniedException>
  <Message>Unable to determine service/operation name to be authorized</Message>
</AccessDeniedException>

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "/workspace/.pyenv_mirror/user/3.12.3/lib/python3.12/site-packages/litellm/utils.py", line 1205, in wrapper
    result = original_function(*args, **kwargs)
             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/workspace/.pyenv_mirror/user/3.12.3/lib/python3.12/site-packages/litellm/main.py", line 3427, in completion
    raise exception_type(
          ^^^^^^^^^^^^^^^
  File "/workspace/.pyenv_mirror/user/3.12.3/lib/python3.12/site-packages/litellm/litellm_core_utils/exception_mapping_utils.py", line 2301, in exception_type
    raise e
  File "/workspace/.pyenv_mirror/user/3.12.3/lib/python3.12/site-packages/litellm/litellm_core_utils/exception_mapping_utils.py", line 528, in exception_type
    raise APIError(
litellm.exceptions.APIError: litellm.APIError: APIError: OpenAIException - <AccessDeniedException>
  <Message>Unable to determine service/operation name to be authorized</Message>
</AccessDeniedException>

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "/workspace/.pyenv_mirror/user/3.12.3/lib/python3.12/site-packages/dspy/adapters/json_adapter.py", line 69, in __call__
    return super().__call__(lm, lm_kwargs, signature, demos, inputs)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/workspace/.pyenv_mirror/user/3.12.3/lib/python3.12/site-packages/dspy/adapters/chat_adapter.py", line 50, in __call__
    raise e
  File "/workspace/.pyenv_mirror/user/3.12.3/lib/python3.12/site-packages/dspy/adapters/chat_adapter.py", line 42, in __call__
    return super().__call__(lm, lm_kwargs, signature, demos, inputs)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/workspace/.pyenv_mirror/user/3.12.3/lib/python3.12/site-packages/dspy/adapters/base.py", line 119, in __call__
    outputs = lm(messages=inputs, **lm_kwargs)
              ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/workspace/.pyenv_mirror/user/3.12.3/lib/python3.12/site-packages/dspy/utils/callback.py", line 326, in sync_wrapper
    return fn(instance, *args, **kwargs)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/workspace/.pyenv_mirror/user/3.12.3/lib/python3.12/site-packages/dspy/clients/base_lm.py", line 96, in __call__
    response = self.forward(prompt=prompt, messages=messages, **kwargs)
               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/workspace/.pyenv_mirror/user/3.12.3/lib/python3.12/site-packages/dspy/clients/lm.py", line 127, in forward
    results = completion(
              ^^^^^^^^^^^
  File "/workspace/.pyenv_mirror/user/3.12.3/lib/python3.12/site-packages/dspy/clients/cache.py", line 232, in sync_wrapper
    result = fn(*args, **kwargs)
             ^^^^^^^^^^^^^^^^^^^
  File "/workspace/.pyenv_mirror/user/3.12.3/lib/python3.12/site-packages/dspy/clients/lm.py", line 304, in litellm_completion
    return litellm.completion(
           ^^^^^^^^^^^^^^^^^^^
  File "/workspace/.pyenv_mirror/user/3.12.3/lib/python3.12/site-packages/litellm/utils.py", line 1310, in wrapper
    return litellm.completion_with_retries(*args, **kwargs)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/workspace/.pyenv_mirror/user/3.12.3/lib/python3.12/site-packages/litellm/main.py", line 3465, in completion_with_retries
    return retryer(original_function, *args, **kwargs)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/workspace/.pyenv_mirror/user/3.12.3/lib/python3.12/site-packages/tenacity/__init__.py", line 477, in __call__
    do = self.iter(retry_state=retry_state)
         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/workspace/.pyenv_mirror/user/3.12.3/lib/python3.12/site-packages/tenacity/__init__.py", line 378, in iter
    result = action(retry_state)
             ^^^^^^^^^^^^^^^^^^^
  File "/workspace/.pyenv_mirror/user/3.12.3/lib/python3.12/site-packages/tenacity/__init__.py", line 420, in exc_check
    raise retry_exc.reraise()
          ^^^^^^^^^^^^^^^^^^^
  File "/workspace/.pyenv_mirror/user/3.12.3/lib/python3.12/site-packages/tenacity/__init__.py", line 187, in reraise
    raise self.last_attempt.result()
          ^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/home/gitpod/.pyenv/versions/3.12.3/lib/python3.12/concurrent/futures/_base.py", line 449, in result
    return self.__get_result()
           ^^^^^^^^^^^^^^^^^^^
  File "/home/gitpod/.pyenv/versions/3.12.3/lib/python3.12/concurrent/futures/_base.py", line 401, in __get_result
    raise self._exception
  File "/workspace/.pyenv_mirror/user/3.12.3/lib/python3.12/site-packages/tenacity/__init__.py", line 480, in __call__
    result = fn(*args, **kwargs)
             ^^^^^^^^^^^^^^^^^^^
  File "/workspace/.pyenv_mirror/user/3.12.3/lib/python3.12/site-packages/litellm/utils.py", line 1330, in wrapper
    raise e
  File "/workspace/.pyenv_mirror/user/3.12.3/lib/python3.12/site-packages/litellm/utils.py", line 1205, in wrapper
    result = original_function(*args, **kwargs)
             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/workspace/.pyenv_mirror/user/3.12.3/lib/python3.12/site-packages/litellm/main.py", line 3427, in completion
    raise exception_type(
          ^^^^^^^^^^^^^^^
  File "/workspace/.pyenv_mirror/user/3.12.3/lib/python3.12/site-packages/litellm/litellm_core_utils/exception_mapping_utils.py", line 2301, in exception_type
    raise e
  File "/workspace/.pyenv_mirror/user/3.12.3/lib/python3.12/site-packages/litellm/litellm_core_utils/exception_mapping_utils.py", line 528, in exception_type
    raise APIError(
litellm.exceptions.APIError: litellm.APIError: APIError: OpenAIException - <AccessDeniedException>
  <Message>Unable to determine service/operation name to be authorized</Message>
</AccessDeniedException>

The above exception was the direct cause of the following exception:

Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "/workspace/.pyenv_mirror/user/3.12.3/lib/python3.12/site-packages/dspy/predict/predict.py", line 85, in __call__
    return super().__call__(**kwargs)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/workspace/.pyenv_mirror/user/3.12.3/lib/python3.12/site-packages/dspy/utils/callback.py", line 326, in sync_wrapper
    return fn(instance, *args, **kwargs)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/workspace/.pyenv_mirror/user/3.12.3/lib/python3.12/site-packages/dspy/primitives/program.py", line 60, in __call__
    return self.forward(*args, **kwargs)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/workspace/.pyenv_mirror/user/3.12.3/lib/python3.12/site-packages/dspy/predict/predict.py", line 157, in forward
    completions = adapter(lm, lm_kwargs=config, signature=signature, demos=demos, inputs=kwargs)
                  ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/workspace/.pyenv_mirror/user/3.12.3/lib/python3.12/site-packages/dspy/adapters/chat_adapter.py", line 51, in __call__
    return JSONAdapter()(lm, lm_kwargs, signature, demos, inputs)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/workspace/.pyenv_mirror/user/3.12.3/lib/python3.12/site-packages/dspy/adapters/json_adapter.py", line 75, in __call__
    raise RuntimeError(
RuntimeError: Both structured output format and JSON mode failed. Please choose a model that supports `response_format` argument. Original error: litellm.APIError: APIError: OpenAIException - <AccessDeniedException>
  <Message>Unable to determine service/operation name to be authorized</Message>
</AccessDeniedException>

Which seems to indicate it hits the openai endpoint instead of my lambda function. If I change it to the below dspy.LM setup, it does hit my custom transport layer (output not shown but it gives an error as non-existing-model does not exist) - probably related to this: https://github.com/stanfordnlp/dspy/blob/cd9ae6e6334c3bcab88fcf387ab9e1aea321f991/dspy/clients/openai.py#L99-L100

lm = dspy.LM(
    model = "openai/non-existing-model", 
    api_key="not-used", 
    api_base=settings["GENAI_GATEWAY_LAMBDA_VPC_ENDPOINT"], 
    provider='openai')

I could bypass this in my own GenAI_AWSSigV4Transport to make sure model = "openai/dspy-gpt-4o-mini" goes to "openai/gpt-4o-mini" but maybe there is a better way?

jwijffels avatar Jul 28 '25 21:07 jwijffels