Skip to content

feat(model): add generic OpenAI-compatible LLM backend#115

Merged
Yif-Yang merged 6 commits into
microsoft:mainfrom
nankingjing:contrib/add-openai-compatible-backend
Jul 14, 2026
Merged

feat(model): add generic OpenAI-compatible LLM backend#115
Yif-Yang merged 6 commits into
microsoft:mainfrom
nankingjing:contrib/add-openai-compatible-backend

Conversation

@nankingjing

Copy link
Copy Markdown
Contributor

Summary

Adds a generic openai_compatible model backend
(skillopt/model/openai_compatible_backend.py) so SkillOpt can talk to any
service that exposes an OpenAI-compatible /chat/completions endpoint using
nothing more than a base_url and an api_key.

A single backend therefore unlocks a large family of providers, including:

  • DeepSeek (https://api.deepseek.com/v1)
  • Groq (https://api.groq.com/openai/v1)
  • Together AI (https://api.together.xyz/v1)
  • Ollama (http://localhost:11434/v1)
  • vLLM / SGLang / TGI self-hosted servers
  • LiteLLM proxy (http://localhost:4000)
  • OpenRouter, Fireworks, Mistral, xAI Grok, Perplexity, and OpenAI itself

What's included

  • New backend module built on the official openai SDK, mirroring the
    callable surface of the existing chat backends (qwen_backend,
    minimax_backend): chat_optimizer, chat_target,
    chat_optimizer_messages, chat_target_messages, plus token tracking,
    configure_openai_compatible(...), and the standard setter functions.
  • Per-role config (optimizer/target) via configure_openai_compatible(...)
    or environment variables (OPENAI_COMPATIBLE_BASE_URL,
    OPENAI_COMPATIBLE_API_KEY, OPENAI_COMPATIBLE_MODEL, plus
    _TEMPERATURE/_MAX_TOKENS/_TIMEOUT_SECONDS, with OPTIMIZER_*/TARGET_*
    overrides). The API key is optional so keyless local servers (Ollama, vLLM)
    work out of the box.
  • count_tokens() using tiktoken when available, with a character-based
    estimate as a fallback for non-OpenAI models.
  • Tool / function calling support through the *_messages(..., tools=...)
    entry points, returning the shared CompatAssistantMessage objects.
  • Retries with exponential backoff and usage recorded through the shared
    TokenTracker.

Registry / routing wiring

  • common.py — registered openai_compatible in _BACKEND_DEFAULT_MODELS and
    _BACKEND_ALIASES (aliases: openai_compatible, openai_compatible_chat,
    openai-compatible, compat).
  • backend_config.py — accepted as a valid optimizer/target chat backend.
  • model/__init__.py — routed through chat_optimizer/chat_target and their
    _messages variants, added to token-summary aggregation and the
    reset/setter helpers, exposed configure_openai_compatible, and wired into
    the legacy set_backend("openai_compatible") shorthand.

Usage

import skillopt.model as model

model.set_backend("openai_compatible")
model.configure_openai_compatible(
    base_url="https://api.deepseek.com/v1",
    api_key="sk-...",
    model="deepseek-chat",
)

or purely via env vars:

export TARGET_BACKEND=openai_compatible
export OPENAI_COMPATIBLE_BASE_URL="https://api.groq.com/openai/v1"
export OPENAI_COMPATIBLE_API_KEY="gsk_..."
export OPENAI_COMPATIBLE_MODEL="llama-3.3-70b-versatile"

Docs

docs/guide/new-backend.md now opens with a section describing this built-in
backend so contributors reach for it before writing a bespoke one.

Testing

  • python -m py_compile on all touched modules.
  • Local import of skillopt.model and backend selection
    (set_backend/get_backend_name/get_optimizer_backend).
  • Functional test with a mocked OpenAI client: verified routing, that the
    configured model/max_tokens are sent, CompatAssistantMessage return
    values, token-summary aggregation, and count_tokens() on both the tiktoken
    and character-fallback paths.

Note: the existing docs/guide/new-backend.md template describes an
aspirational ModelBackend/async generate() interface that does not match
the current module-based backends. This backend intentionally follows the
actual in-tree pattern so it integrates and runs today; the added docs
section reflects the real interface.

@nankingjing

Copy link
Copy Markdown
Contributor Author

@microsoft-github-policy-service agree

@Yif-Yang

Copy link
Copy Markdown
Contributor

Thanks — a generic OpenAI-compatible backend is a meaningful usability improvement, and we would like to land it through this original PR so the contribution remains attributed to you.

The PR now conflicts with newer backend registry/configuration changes. Could you please rebase onto the latest main and resolve the conflicts as a union rather than replacing the current backend lists?

Acceptance points:

  • Keep all current Azure/OpenAI, Claude, Qwen, MiniMax, Codex-exec, and Claude-code-exec paths.
  • Add openai_compatible for both optimizer and target routing.
  • Preserve role-specific endpoint/key/model configuration.
  • Ensure get_token_summary() aggregates each backend exactly once.
  • Keep imports lazy enough that users of other backends do not require OpenAI-compatible credentials.

We tested a local union resolution with 47 model-focused tests plus the full suite passing. Please add or retain focused routing/config/token tests when rebasing, then ping us for a prompt re-review. We prefer merging this PR rather than replacing it, to preserve your authorship and PR history.

@nankingjing
nankingjing force-pushed the contrib/add-openai-compatible-backend branch from 7012ab7 to 45ff88a Compare July 14, 2026 01:22
@nankingjing

Copy link
Copy Markdown
Contributor Author

Rebased onto the latest main and resolved the conflicts as a union, preserving all existing Azure/OpenAI, Claude, Qwen, MiniMax, Codex-exec, and Claude-code-exec paths.

Changes made:

  • Kept every existing backend dispatch and token tracker path in skillopt/model/__init__.py.
  • Added openai_compatible routing for both optimizer and target chat paths (and updated unsupported-backend error messages).
  • Retained role-specific endpoint/key/model configuration via configure_openai_compatible.
  • Kept openai_compatible lazy: clients are built only when first requested, and the backend imports only the shared OpenAI SDK.
  • Added tests/test_openai_compatible_backend.py covering role-specific config, optimizer/target routing, client laziness, and token-summary aggregation.

Validation:

  • 23 model-focused tests passed (new test file + test_qwen_backend.py + test_handoff_backend.py).
  • Broader unit test run: 242 passed, 3 skipped, 5 failed. The 5 failures are in test_run_sleep_fallback.py and reproduce on a clean checkout of main in this Windows environment (path/codec issues unrelated to this backend change).

Let me know if you’d like any adjustments before re-review.

@nankingjing

Copy link
Copy Markdown
Contributor Author

The branch is already rebased on the latest main (0 commits behind) and remains mergeable. Here is a fresh verification:

Union resolution — All existing paths preserved:

  • Azure/OpenAI, Claude, Qwen, MiniMax, Codex-exec, Claude-code-exec — all intact
  • openai_compatible added for both optimizer and target routing in chat_optimizer, chat_target, and *_messages variants
  • Role-specific config via configure_openai_compatible(...) with optimizer/target overrides
  • get_token_summary() aggregates all 5 backends exactly once each
  • Lazy imports preserved — openai_compatible_backend only imports the shared openai SDK

Test results:

  • 23 focused model tests (new test_openai_compatible_backend.py + test_qwen_backend.py + test_handoff_backend.py) — all pass
  • Full suite: 261 passed, 2 skipped, 1 failed (pre-existing test_alfworld_paths.py path separator issue on Windows, unrelated)

Ready for re-review.

@Yif-Yang
Yif-Yang merged commit 7bf9ecb into microsoft:main Jul 14, 2026
1 check passed
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.

2 participants