Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
17 commits
Select commit Hold shift + click to select a range
bc4b7fd
feat(decisions): add unified /v1/decisions endpoint for Jev-compatibl…
devin-ai-integration[bot] Oct 7, 2026
c24cbf6
feat(decisions): serve System One format at /v1/systemone and OpenAI …
mateo-berri Oct 7, 2026
77d57c3
fix(decisions): import assert_never from typing_extensions for Python…
mateo-berri Oct 7, 2026
5f4588d
fix(decisions): cap OpenAI-format questions at the System One limit
mateo-berri Oct 7, 2026
d9afde2
fix(decisions): return 400 for bodies that are not JSON
mateo-berri Oct 7, 2026
ef6cbbc
bump: version 1.104.2
mateo-berri Oct 7, 2026
138f901
feat(decisions): add OpenAI as a Decisions provider
mateo-berri Oct 8, 2026
fbcc73b
refactor(decisions): move the OpenAI wire translation into llms/openai
mateo-berri Oct 8, 2026
637cc3f
feat(decisions): route both request formats through a shared decision…
mateo-berri Oct 8, 2026
df8f46c
test(decisions): read _hidden_params in the OpenAI provider test on s…
mateo-berri Oct 8, 2026
2976595
fix(decisions): keep provider fields inside System One answers
mateo-berri Oct 8, 2026
36894a1
fix(decisions): give IR extra fields a default_factory so Python 3.11…
mateo-berri Oct 8, 2026
7df9c06
fix(decisions): load on Python 3.10 and 3.11 and bill cached tokens o…
mateo-berri Oct 8, 2026
c65d369
fix(decisions): honor litellm OpenAI key and base settings for the op…
mateo-berri Oct 8, 2026
5d2cc10
test(decisions): clear every OpenAI base setting in the proxy OpenAI …
mateo-berri Oct 8, 2026
f0cb1d5
fix(decisions): send OpenAI instructions for System One questions wit…
mateo-berri Oct 8, 2026
7446e02
fix(decisions): return guardrail_information on OpenAI-format decisio…
mateo-berri Oct 8, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion .github/workflows/test-unit.yml
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@ jobs:

- shard: core-utils
artifact-name: core-utils
test-path: ""
test-path: tests/unit/decisions
unit-flag: core-utils
workers: 2
reruns: 1
Expand Down Expand Up @@ -130,6 +130,7 @@ jobs:
artifact-name: proxy-endpoints
test-path: >-
tests/test_litellm/proxy/analytics_endpoints
tests/unit/proxy/decisions_endpoints
tests/test_litellm/proxy/management_endpoints
tests/test_litellm/proxy/list_api
tests/test_litellm/proxy/memory
Expand Down
2 changes: 2 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -364,11 +364,13 @@ For MCP OAuth, an upstream may advertise dynamic client registration but refuse
| [Sagemaker Chat (`sagemaker_chat`)](https://docs.litellm.ai/docs/providers/aws_sagemaker) | ✅ | ✅ | ✅ | | | | | | | |
| [Sambanova (`sambanova`)](https://docs.litellm.ai/docs/providers/sambanova) | ✅ | ✅ | ✅ | | | | | | | |
| [Snowflake (`snowflake`)](https://docs.litellm.ai/docs/providers/snowflake) | ✅ | ✅ | ✅ | | | | | | | |
| [Strands Decider (`strands_decider`)](https://docs.litellm.ai/docs/providers) | | | | | | | | | | |
| [Text Completion Codestral (`text-completion-codestral`)](https://docs.litellm.ai/docs/providers/codestral) | ✅ | ✅ | ✅ | | | | | | | |
| [Text Completion OpenAI (`text-completion-openai`)](https://docs.litellm.ai/docs/providers/text_completion_openai) | ✅ | ✅ | ✅ | | | ✅ | ✅ | ✅ | ✅ | |
| [Together AI (`together_ai`)](https://docs.litellm.ai/docs/providers/togetherai) | ✅ | ✅ | ✅ | | | | | | | |
| [Topaz (`topaz`)](https://docs.litellm.ai/docs/providers/topaz) | ✅ | ✅ | ✅ | | | | | | | |
| [Triton (`triton`)](https://docs.litellm.ai/docs/providers/triton-inference-server) | ✅ | ✅ | ✅ | | | | | | | |
| [Typesafe Decisions API (`typesafe`)](https://docs.litellm.ai/docs/providers) | | | | | | | | | | |
| [V0 (`v0`)](https://docs.litellm.ai/docs/providers/v0) | ✅ | ✅ | ✅ | | | | | | | |
| [Vercel AI Gateway (`vercel_ai_gateway`)](https://docs.litellm.ai/docs/providers/vercel_ai_gateway) | ✅ | ✅ | ✅ | | | | | | | |
| [VLLM (`vllm`)](https://docs.litellm.ai/docs/providers/vllm) | ✅ | ✅ | ✅ | | | | | | | |
Expand Down
6 changes: 5 additions & 1 deletion gateway/routes/allowlist.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
"""Path allowlist for the gateway component.

The gateway exposes the LLM data-plane surface: chat/completions, embeddings,
audio, batches, files, fine-tuning, rerank, ocr, rag, video, search, image,
audio, batches, files, fine-tuning, rerank, decisions, ocr, rag, video, search, image,
responses, vector stores, passthrough providers, realtime websockets, MCP
tool-call endpoints, and operational endpoints (/health, /metrics, and the
/debug/memory/summary read of the serving worker's RSS).
Expand Down Expand Up @@ -60,6 +60,10 @@
"/v1/rerank",
"/v2/rerank",
"/rerank",
"/v1/decisions",
"/decisions",
"/v1/systemone",
"/systemone",
"/v1/ocr",
"/ocr",
"/v1/rag/",
Expand Down
1 change: 1 addition & 0 deletions litellm/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -1467,6 +1467,7 @@ def _build_models_by_provider() -> dict:
from .rust_bridge import rust
from .rag.main import *
from .sandbox.main import *
from .decisions.main import *
from .search.main import *
from .realtime_api.main import (
_arealtime,
Expand Down
41 changes: 38 additions & 3 deletions litellm/cost_calculator.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@

from httpx import Response
from pydantic import BaseModel
from typing_extensions import ReadOnly, TypedDict
from typing_extensions import ReadOnly, TypedDict, assert_never

import litellm
import litellm._logging
Expand Down Expand Up @@ -98,6 +98,7 @@
from litellm.llms.xai.cost_calculator import cost_per_token as xai_cost_per_token
from litellm.responses.utils import ResponseAPILoggingUtils
from litellm.types.agents import LiteLLMSendMessageResponse
from litellm.types.decisions import DecisionsResponse, DecisionsUsage, OpenAIDecisionResponse, OpenAIDecisionUsage
from litellm.types.llms.base import CachedTokensDetails
from litellm.types.llms.openai import (
HttpxBinaryResponseContent,
Expand Down Expand Up @@ -981,6 +982,8 @@ def get_usage_object(
),
)

if isinstance(completion_response, (DecisionsResponse, OpenAIDecisionResponse)):
return None if completion_response.usage is None else _decisions_usage(completion_response.usage)
if usage_obj is None:
return None
if isinstance(usage_obj, Usage):
Expand Down Expand Up @@ -1011,11 +1014,37 @@ def get_usage_object(
return None


def _decisions_prompt_tokens_details(usage: DecisionsUsage | OpenAIDecisionUsage) -> PromptTokensDetailsWrapper:
match usage:
case DecisionsUsage():
return PromptTokensDetailsWrapper(
cached_tokens=usage.cached_tokens, cache_write_tokens=usage.cache_write_tokens
)
case OpenAIDecisionUsage():
return PromptTokensDetailsWrapper(
cached_tokens=usage.input_tokens_details.cached_tokens,
cache_write_tokens=usage.input_tokens_details.cache_write_tokens,
)
case _:
assert_never(usage)


def _decisions_usage(usage: DecisionsUsage | OpenAIDecisionUsage) -> Usage:
return Usage(
prompt_tokens=usage.input_tokens,
completion_tokens=usage.output_tokens,
total_tokens=usage.input_tokens + usage.output_tokens,
prompt_tokens_details=_decisions_prompt_tokens_details(usage),
)


def _is_known_usage_objects(usage_obj):
"""Returns True if the usage obj is a known Usage type"""
return (
isinstance(usage_obj, litellm.Usage)
or isinstance(usage_obj, ResponseAPIUsage)
or isinstance(usage_obj, DecisionsUsage)
or isinstance(usage_obj, OpenAIDecisionUsage)
or TranscriptionUsageObjectTransformation.is_transcription_usage_object(usage_obj)
)

Expand Down Expand Up @@ -1433,7 +1462,9 @@ def completion_cost(
"usage",
litellm.Usage(**_usage_for_dump.model_dump()),
)
if usage_obj is None:
if isinstance(usage_obj, (DecisionsUsage, OpenAIDecisionUsage)):
_usage = _decisions_usage(usage_obj).model_dump()
elif usage_obj is None:
_usage = {}
elif isinstance(usage_obj, BaseModel):
_usage = cast(BaseModel, usage_obj).model_dump()
Expand Down Expand Up @@ -1933,7 +1964,9 @@ def response_cost_calculator(
| LiteLLMRealtimeStreamLoggingObject
| OpenAIModerationResponse
| Response
| SearchResponse,
| SearchResponse
| DecisionsResponse
| OpenAIDecisionResponse,
model: str,
custom_llm_provider: str | None,
call_type: Literal[
Expand All @@ -1955,6 +1988,8 @@ def response_cost_calculator(
"arerank",
"search",
"asearch",
"decisions",
"adecisions",
],
optional_params: dict,
cache_hit: bool | None = None,
Expand Down
3 changes: 3 additions & 0 deletions litellm/decisions/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
from litellm.decisions.main import adecisions, decisions

__all__ = ["adecisions", "decisions"]
Loading
Loading