Skip to content

feat: add protocol= + base_url= for compatible APIs#241

Merged
Kamilbenkirane merged 2 commits intomainfrom
feat/protocol-base-url-v2
Mar 27, 2026
Merged

feat: add protocol= + base_url= for compatible APIs#241
Kamilbenkirane merged 2 commits intomainfrom
feat/protocol-base-url-v2

Conversation

@Kamilbenkirane
Copy link
Copy Markdown
Member

Summary

  • Add protocol= + base_url= parameters to create_client() and domain namespaces for pointing celeste at any compatible API (MiniMax, vLLM, Ollama, LocalAI, etc.)
  • Separate provider and protocol as independent fields on ModalityClient — a protocol is NOT a provider
  • Wire base_url through to _build_url() as a client instance attribute
  • BYOA auth for protocol path (defaults to NoAuth, accepts api_key= or auth=)
  • Remove dead base_url plumbing from per-call method signatures

Usage

# OpenAI-compatible (openresponses protocol)
await celeste.text.generate("Hello", model="lfm2:latest", protocol="openresponses", base_url="http://localhost:11434")

# OpenAI Chat Completions-compatible (e.g. MiniMax, vLLM)
await celeste.text.generate("Hello", model="MiniMax-M2.5", protocol="chatcompletions", base_url="https://api.minimax.io", api_key="XX-YOUR-API-KEY")

# Provider with custom base URL
client = create_client(provider="openai", base_url="https://my-proxy.com", model="gpt-4o", modality="text")

Design decisions

  • protocol replaces provider for the compatible API case — same concept (wire format identity) without naming a company
  • ModalityClient.provider: Provider | None — None for protocol-only clients
  • ModalityClient.protocol: Protocol | None — None for provider-only clients
  • Model.provider: Provider | None — None for protocol-path models (standard Python Optional pattern)
  • base_url without protocol defaults to openresponses (most common compatible format)

Test plan

  • 527 unit tests pass
  • 15 new tests for protocol + base_url paths
  • mypy passes (0 errors, no type:ignore on new code)
  • Smoke tested with local Ollama (lfm2:latest via protocol="openresponses")
  • Smoke tested provider path (provider="ollama") still works

Closes #230
Closes #237

Co-Authored-By: Xinyue 7869833+XinyueZ@users.noreply.github.com

Add support for pointing celeste at any OpenAI-compatible or
Anthropic-compatible API using protocol= and base_url= parameters.

- Add Protocol enum (openresponses, chatcompletions) to core.py
- Remove Provider.OPENRESPONSES (protocols aren't providers)
- Separate provider and protocol as independent fields on ModalityClient
- Wire base_url as instance field, used by _build_url()
- Add protocol= and base_url= to create_client() and domain namespaces
- Default to openresponses when base_url given without protocol
- BYOA auth: protocol path defaults to NoAuth, accepts api_key or auth
- Make Model.provider optional (None for protocol-path models)
- Remove dead base_url plumbing from per-call method signatures
- Fix credentials.get_auth to use api_key without registry entry
- Clean up OllamaGenerateClient to use _build_url() pattern
- Remove redundant .value on StrEnum in integration tests

Closes #230
StrEnum values are already strings — the | str union on Modality,
Operation, Protocol, and Provider parameters was unnecessary.
Also remove isinstance(x, str) coercions that are no-ops on StrEnums.
@Kamilbenkirane Kamilbenkirane merged commit 6ebc51e into main Mar 27, 2026
11 checks passed
@claude
Copy link
Copy Markdown

claude bot commented Mar 27, 2026

Code review

Bug: provider is dropped when provider= is not passed explicitly (src/celeste/init.py:276-283)

When create_client() is called without an explicit provider= argument (the common case — e.g. create_client(model="gpt-4o", modality=Modality.TEXT)), resolved_provider is None. The client is then constructed with both provider=None and protocol=None. When any HTTP call is made, ModalityClient.http_client (client.py:175-181) raises ClientNotFoundError because it checks self.provider first, then self.protocol, and both are None.

Before this PR the line read provider=resolved_model.provider, which always carried the provider derived from model resolution. The change to provider=resolved_provider breaks the standard provider path any time the caller omits provider=.

Fix: change line 279 to:

        provider=resolved_provider or resolved_model.provider,

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Remove redundant | str from StrEnum parameters in create_client Wire base_url + protocol through to protocol clients

1 participant