Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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
10 changes: 8 additions & 2 deletions composer/input/files.py
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,11 @@

class ContentRenderer(Protocol):
def text_block(self, text: str, *, cache_level: CacheLevel = CacheLevel.NONE) -> dict: ...
def file_block(self, file_id: str, *, cache_level: CacheLevel = CacheLevel.NONE) -> dict: ...
# ``filename`` is redundant for the Files-API providers, which reference an
# upload by id alone, but is required by a renderer that inlines the bytes
# as a data URL (OpenRouter): the content block carries no id to look a name
# up by, and OpenAI-compatible inline file blocks require ``filename``.
def file_block(self, file_id: str, *, filename: str, cache_level: CacheLevel = CacheLevel.NONE) -> dict: ...

# ---------------------------------------------------------------------------
# Protocols (the public surface)
Expand Down Expand Up @@ -224,7 +228,9 @@ class UploadedFile:
renderer: ContentRenderer

def to_dict(self, cache_level: CacheLevel = CacheLevel.NONE) -> dict:
return self.renderer.file_block(file_id=self.file_id, cache_level=cache_level)
return self.renderer.file_block(
file_id=self.file_id, filename=self.basename, cache_level=cache_level
)

def to_digest(self) -> str:
return self.digest
Expand Down
18 changes: 8 additions & 10 deletions composer/llm/anthropic.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
from composer.input.files import UploaderBase, ContentRenderer
from composer.input.types import ModelConfiguration
from composer.llm.provider import (
ProviderServiceBase, ProviderSpec, compaction_threshold
ProviderServiceBase, ProviderSpec, compaction_threshold, standard_callbacks
)
from composer.llm.pricing import PriceProvider, price_provider_for
from .types import CacheLevel
Expand Down Expand Up @@ -137,7 +137,10 @@ def text_block(self, text: str, *, cache_level: CacheLevel = CacheLevel.NONE) ->
}
return to_ret

def file_block(self, file_id: str, *, cache_level: CacheLevel = CacheLevel.NONE) -> dict:
def file_block(
self, file_id: str, *, filename: str, cache_level: CacheLevel = CacheLevel.NONE
) -> dict:
# filename unused: the Files API upload already carries it.
to_ret : dict[str, Any] = {
"type": "document",
"source": {
Expand Down Expand Up @@ -263,8 +266,6 @@ def builder_for(
self, *, cache_level: CacheLevel = CacheLevel.NONE, disable_thinking: bool = False
) -> "BaseChatModel":
from langchain_anthropic import ChatAnthropic
from composer.diagnostics.usage_callback import UsageCallback
from composer.diagnostics.cost_callback import CostAccumulator

opts = self.options
thinking: dict[str, Any] | None
Expand Down Expand Up @@ -299,12 +300,9 @@ def builder_for(
betas=betas,
thinking=thinking,
model_kwargs=model_kwargs,
callbacks=[
UsageCallback(),
CostAccumulator(
self.price_provider, long_cache=cache_level == CacheLevel.LONG
),
],
callbacks=standard_callbacks(
self.price_provider, long_cache=cache_level == CacheLevel.LONG
),
)

ANTHROPIC_SPEC = ProviderSpec(
Expand Down
22 changes: 8 additions & 14 deletions composer/llm/openai.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,8 @@
from composer.input.files import UploaderBase, ContentRenderer
from composer.input.types import ModelConfiguration
from .provider import (
ProviderServiceBase, ProviderSpec, compaction_threshold
ProviderServiceBase, ProviderSpec, compaction_threshold, reasoning_effort,
standard_callbacks
)
from .pricing import PriceProvider, price_provider_for
from .types import CacheLevel
Expand Down Expand Up @@ -135,14 +136,6 @@ def _context_window(features: OpenAIModelFeatures) -> int:
return _assumed_context_window


def _reasoning_effort(thinking_tokens: int) -> Literal["low", "medium", "high"]:
"""Map a thinking-token budget onto OpenAI's three-step effort knob."""
if thinking_tokens <= 2048:
return "low"
if thinking_tokens <= 8192:
return "medium"
return "high"

class OpenAIService(ProviderServiceBase):
def __init__(self):
from graphcore.tools.memory import openai_async_memory_tool
Expand All @@ -168,7 +161,10 @@ class OpenAIRenderer:
def text_block(self, text: str, *, cache_level: CacheLevel = CacheLevel.NONE) -> dict:
to_ret : dict[str, Any] = {"type": "text", "text": text}
return to_ret
def file_block(self, file_id: str, *, cache_level: CacheLevel = CacheLevel.NONE) -> dict:
def file_block(
self, file_id: str, *, filename: str, cache_level: CacheLevel = CacheLevel.NONE
) -> dict:
# filename unused: the Files API upload already carries it.
return {
"type": "file",
"file": {
Expand Down Expand Up @@ -250,8 +246,6 @@ def builder_for(
self, *, cache_level: CacheLevel = CacheLevel.NONE, disable_thinking: bool = False
) -> "BaseChatModel":
from langchain_openai import ChatOpenAI
from composer.diagnostics.usage_callback import UsageCallback
from composer.diagnostics.cost_callback import CostAccumulator

opts = self.options
kwargs: dict[str, Any] = {
Expand All @@ -262,7 +256,7 @@ def builder_for(

if opts.thinking_tokens is not None and not disable_thinking and self.features.reasoning:
kwargs["reasoning"] = {
"effort": _reasoning_effort(opts.thinking_tokens),
"effort": reasoning_effort(opts.thinking_tokens),
"summary": "auto"
}

Expand All @@ -273,7 +267,7 @@ def builder_for(
max_retries=2,
# OpenAI has no cache-TTL knob, so long_cache stays False; cache_write_1h
# mirrors cache_write in the table anyway.
callbacks=[UsageCallback(), CostAccumulator(self.price_provider)],
callbacks=standard_callbacks(self.price_provider),
**kwargs,
)

Expand Down
Loading
Loading