Skip to content

Feat/add litellm provider - #913

Open
RheagalFire wants to merge 2 commits into
cloudwego:mainfrom
RheagalFire:feat/add-litellm-provider
Open

Feat/add litellm provider#913
RheagalFire wants to merge 2 commits into
cloudwego:mainfrom
RheagalFire:feat/add-litellm-provider

Conversation

@RheagalFire

Copy link
Copy Markdown

What type of PR is this?

feat, tests

Check the PR title.

  • This PR title match the format: <type>(optional scope): <description>
  • The description of this PR title is user-oriented and clear enough for others to understand.
  • Attach the PR updating the user documentation if the current PR requires user awareness at the usage level. User docs repo

(Optional) Translate the PR title into Chinese.

(Optional) More detailed description for this PR(en: English/zh: Chinese).

Summary

Adds a new components/model/litellm ChatModel provider that connects to a LiteLLM proxy, giving Eino users access to 100+ LLM providers (OpenAI, Anthropic, Google Gemini, AWS Bedrock, Azure OpenAI, Mistral, Cohere, and more) through a single component.

LiteLLM is an open-source AI gateway where users configure all their models and API keys on the proxy server. A single LiteLLM ChatModel component lets Eino users reach any of these providers without waiting for individual provider modules to be added upstream.

Changes

  • components/model/litellm/chatmodel.go - New ChatModel wrapping libs/acl/openai with LiteLLM proxy config
  • components/model/litellm/option.go - Per-request options (WithExtraFields, WithExtraHeader)
  • components/model/litellm/chatmodel_test.go - Unit tests (10 test cases covering Generate, Stream, BindTools, WithTools, error propagation)
  • components/model/litellm/go.mod - Independent Go module
  • components/model/litellm/README.md - Documentation with usage examples
  • components/model/litellm/examples/ - Generate and stream examples

Architecture

The provider follows (same as openrouter, openai): a thin wrapper around libs/acl/openai.Client. LiteLLM proxy normalizes all provider responses to OpenAI format server-side, so no custom RequestPayloadModifier or ResponseMessageModifier is needed. This keeps the implementation minimal and maintainable.

LiteLLM-specific features (metadata, tags, drop_params, force_timeout) are passed via the existing ExtraFields config.

Tests

Unit tests (all passing):

TestNewChatModel/success PASSED
TestNewChatModel/nil_config PASSED
TestNewChatModel/missing_base_url PASSED
TestNewChatModel/custom_http_client PASSED
TestNewChatModel/with_extra_fields PASSED
TestNewChatModel/with_extra_fields_for_drop_params PASSED
TestChatModel_Generate - delegates to client PASSED
TestChatModel_Generate - returns error from client PASSED
TestChatModel_Stream - delegates and reads content PASSED
TestChatModel_Stream - returns error from client PASSED
TestChatModel_WithTools - returns new instance PASSED
TestChatModel_BindTools - delegates to client PASSED
TestChatModel_BindTools - returns error from client PASSED
TestChatModel_GetType PASSED
TestChatModel_WithModelOption PASSED
PASS
ok   github.com/cloudwego/eino-ext/components/model/litellm

gofmt: Clean.

Risk / Compatibility

  • Additive only. No existing providers or shared code modified.
  • New independent Go module - zero impact on existing go.mod files.
  • Reuses the battle-tested libs/acl/openai client for all HTTP, streaming, and tool-calling logic.

Example usage

1. Start a LiteLLM proxy (manages models + API keys server-side):

pip install litellm
litellm --model openai/gpt-4o --model anthropic/claude-sonnet-4-20250514
# Proxy running at http://localhost:4000

2. Connect Eino to the proxy:

package main

import (
	"context"
	"fmt"
	"log"

	"github.com/cloudwego/eino-ext/components/model/litellm"
	"github.com/cloudwego/eino/schema"
)

func main() {
	ctx := context.Background()

	chatModel, err := litellm.NewChatModel(ctx, &litellm.Config{
		BaseURL: "http://localhost:4000",
		APIKey:  "sk-your-litellm-key",
		Model:   "openai/gpt-4o",
	})
	if err != nil {
		log.Fatal(err)
	}

	// Generate
	resp, err := chatModel.Generate(ctx, []*schema.Message{
		{Role: schema.User, Content: "What is 2+2?"},
	})
	if err != nil {
		log.Fatal(err)
	}
	fmt.Println(resp.Content) // "4"
}

3. Streaming:

stream, err := chatModel.Stream(ctx, []*schema.Message{
	{Role: schema.User, Content: "Tell me a joke"},
})
if err != nil {
	log.Fatal(err)
}
defer stream.Close()

for {
	msg, err := stream.Recv()
	if err != nil {
		break
	}
	fmt.Print(msg.Content)
}

4. Tool calling:

modelWithTools, err := chatModel.WithTools([]*schema.ToolInfo{
	{
		Name: "get_weather",
		Desc: "Get current weather for a city",
		ParamsOneOf: schema.NewParamsOneOfByParams(map[string]*schema.ParameterInfo{
			"city": {Type: "string", Desc: "City name"},
		}),
	},
})
if err != nil {
	log.Fatal(err)
}

resp, err := modelWithTools.Generate(ctx, []*schema.Message{
	{Role: schema.User, Content: "What's the weather in Tokyo?"},
})
// resp.ToolCalls contains the function call

5. Pass LiteLLM-specific parameters via ExtraFields:

chatModel, _ := litellm.NewChatModel(ctx, &litellm.Config{
	BaseURL: "http://localhost:4000",
	APIKey:  "sk-key",
	Model:   "anthropic/claude-sonnet-4-20250514",
	ExtraFields: map[string]any{
		"drop_params": true,
		"metadata":    map[string]string{"team": "engineering"},
	},
})

// Or per-request:
resp, err := chatModel.Generate(ctx, messages,
	litellm.WithExtraFields(map[string]any{"drop_params": true}),
	litellm.WithExtraHeader(map[string]string{"X-Custom-Tag": "prod"}),
)

zh(optional):

(Optional) Which issue(s) this PR fixes:

(optional) The PR that updates user documentation:

@CLAassistant

CLAassistant commented Jul 12, 2026

Copy link
Copy Markdown

CLA assistant check
All committers have signed the CLA.

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

Labels

None yet

Development

Successfully merging this pull request may close these issues.

2 participants