Files
Fu-Jie_openwebui-extensions/plugins/debug/byok-infinite-session-research/client-architecture.md
fujie d29c24ba4a feat(openwebui-skills-manager): enhance auto-discovery and structural refactoring
- Enable default overwrite installation policy for overlapping skills
- Support deep recursive GitHub trees discovery mechanism to resolve #58
- Refactor internal architecture to fully decouple stateless helper logic
- READMEs and docs synced (v0.3.0)
2026-03-08 18:21:21 +08:00

9.3 KiB
Raw Blame History

Client传入和管理分析

当前的Client管理架构

┌────────────────────────────────────────┐
│  Pipe Instance (github_copilot_sdk.py)  │
│                                        │
│  _shared_clients = {                   │
│    "token_hash_1": CopilotClient(...), │  ← 基于GitHub Token缓存
│    "token_hash_2": CopilotClient(...), │
│  }                                     │
└────────────────────────────────────────┘
         │
         │ await _get_client(token)
         │
         ▼
┌────────────────────────────────────────┐
│  CopilotClient Instance                │
│                                        │
│  [仅需GitHub Token配置]                │
│                                        │
│  config {                              │
│    github_token: "ghp_...",            │
│    cli_path: "...",                    │
│    config_dir: "...",                  │
│    env: {...},                         │
│    cwd: "..."                          │
│  }                                     │
└────────────────────────────────────────┘
         │
         │ create_session(session_config)
         │
         ▼
┌────────────────────────────────────────┐
│  Session (per-session configuration)   │
│                                        │
│  session_config {                      │
│    model: "real_model_id",             │
│    provider: {                         │ ← ⭐ BYOK配置在这里
│      type: "openai",                   │
│      base_url: "https://api.openai...", 
│      api_key: "sk-...",                │
│      ...                               │
│    },                                  │
│    infinite_sessions: {...},           │
│    system_message: {...},              │
│    ...                                 │
│  }                                     │
└────────────────────────────────────────┘

目前的流程(代码实际位置)

步骤1获取或创建Clientline 6208

# _pipe_impl中
client = await self._get_client(token)

步骤2_get_client函数line 5523-5561

async def _get_client(self, token: str) -> Any:
    """Get or create the persistent CopilotClient from the pool based on token."""
    if not token:
        raise ValueError("GitHub Token is required to initialize CopilotClient")
    
    token_hash = hashlib.md5(token.encode()).hexdigest()
    
    # 查看是否已有缓存的client
    client = self.__class__._shared_clients.get(token_hash)
    if client and client状态正常:
        return client  # ← 复用已有的client
    
    # 否则创建新client
    client_config = self._build_client_config(user_id=None, chat_id=None)
    client_config["github_token"] = token
    new_client = CopilotClient(client_config)
    await new_client.start()
    self.__class__._shared_clients[token_hash] = new_client
    return new_client

步骤3创建会话时传入providerline 6253-6270

# _pipe_impl中BYOK部分
if is_byok_model:
    provider_config = {
        "type": byok_type,          # "openai" or "anthropic"
        "wire_api": byok_wire_api,
        "base_url": byok_base_url,
        "api_key": byok_api_key or None,
        "bearer_token": byok_bearer_token or None,
    }

# 然后传入session config
session = await client.create_session(config={
    "model": real_model_id,
    "provider": provider_config,  # ← provider在这里传给session
    ...
})

关键问题架构的2个层级

层级 用途 配置内容 缓存方式
CopilotClient CLI和运行时底层逻辑 GitHub Token, CLI path, 环境变量 基于token_hash全局缓存
Session 具体的对话会话 Model, Provider(BYOK), Tools, System Prompt 不缓存(每次新建)

当前的问题

问题1Client是全局缓存的但Provider是会话级别的

# ❓ 如果用户想为不同的BYOK模型使用不同的Client呢
# 当前无法做到因为Client基于token缓存是全局的

# 例子:
# Client A: OpenAI API key (token_hash_1)
# Client B: Anthropic API key (token_hash_2)

# 但在Pipe中只有一个GH_TOKEN导致只能有一个Client

问题2Provider和Client是不同的东西

# CopilotClient = GitHub Copilot SDK客户端
# ProviderConfig = OpenAI/Anthropic等的API配置

# 用户可能混淆:
# "怎么传入BYOK的client和provider"
# → 实际上只能传provider到sessionclient是全局的

问题3BYOK模型混用的情况处理不清楚

# 如果用户想在同一个Pipe中
# - Model A 用 OpenAI API
# - Model B 用 Anthropic API
# - Model C 用自己的本地LLM

# 当前代码是基于全局BYOK配置的无法为各模型单独设置

改进方案

方案A保持当前架构只改Provider映射

思路Client保持全局基于GH_TOKEN但Provider配置基于模型动态选择

# 在Valves中添加
class Valves(BaseModel):
    # ... 现有配置 ...
    
    # 新增模型到Provider的映射 (JSON)
    MODEL_PROVIDER_MAP: str = Field(
        default="{}",
        description='Map model IDs to BYOK providers (JSON). Example: '
                    '{"gpt-4": {"type": "openai", "base_url": "...", "api_key": "..."}, '
                    '"claude-3": {"type": "anthropic", "base_url": "...", "api_key": "..."}}'
    )

# 在_pipe_impl中
def _get_provider_config(self, model_id: str, byok_active: bool) -> Optional[dict]:
    """Get provider config for a specific model"""
    if not byok_active:
        return None
    
    try:
        model_map = json.loads(self.valves.MODEL_PROVIDER_MAP or "{}")
        return model_map.get(model_id)
    except:
        return None

# 使用时
provider_config = self._get_provider_config(real_model_id, byok_active) or {
    "type": byok_type,
    "base_url": byok_base_url,
    "api_key": byok_api_key,
    ...
}

优点最小改动复用现有Client架构
缺点多个BYOK模型仍共享一个Client只要GH_TOKEN相同


方案B为不同BYOK提供商创建不同的Client

思路扩展_get_client支持基于provider_type的多client缓存

async def _get_or_create_client(
    self, 
    token: str,
    provider_type: str = "github"  # "github", "openai", "anthropic"
) -> Any:
    """Get or create client based on token and provider type"""
    
    if provider_type == "github" or not provider_type:
        # 现有逻辑
        token_hash = hashlib.md5(token.encode()).hexdigest()
    else:
        # 为BYOK提供商创建不同的client
        composite_key = f"{token}:{provider_type}"
        token_hash = hashlib.md5(composite_key.encode()).hexdigest()
    
    # 从缓存获取或创建
    ...

优点隔离不同BYOK提供商的Client
缺点:更复杂,需要更多改动


建议的改进路线

优先级1方案A - 模型到Provider的映射

添加Valves配置

MODEL_PROVIDER_MAP: str = Field(
    default="{}",
    description='Map specific models to their BYOK providers (JSON format)'
)

使用方式:

{
  "gpt-4": {
    "type": "openai",
    "base_url": "https://api.openai.com/v1",
    "api_key": "sk-..."
  },
  "claude-3": {
    "type": "anthropic",
    "base_url": "https://api.anthropic.com/v1",
    "api_key": "ant-..."
  },
  "llama-2": {
    "type": "openai",  # 开源模型通常使用openai兼容API
    "base_url": "http://localhost:8000/v1",
    "api_key": "sk-local"
  }
}

优先级2在_build_session_config中考虑provider_config

修改infinite_session初始化基于provider_config判断

def _build_session_config(..., provider_config=None):
    # 如果使用了BYOK provider需要特殊处理infinite_session
    infinite_session_config = None
    if self.valves.INFINITE_SESSION and provider_config is None:
        # 仅官方Copilot模型启用compression
        infinite_session_config = InfiniteSessionConfig(...)

优先级3方案B - 多client缓存长期改进

如果需要完全隔离不同BYOK提供商的Client。


总结如果你要传入BYOK client

现状

  • CopilotClient是基于GH_TOKEN全局缓存的
  • Provider配置是在SessionConfig级别动态设置的
  • 一个Client可以创建多个Session每个Session用不同的Provider

改进后

  • 添加MODEL_PROVIDER_MAP配置
  • 对每个模型的请求动态选择对应的Provider配置
  • 同一个Client可以为不同Provider服务不同的models

你需要做的

  1. 在Valves中配置MODEL_PROVIDER_MAP
  2. 在模型选择时读取这个映射
  3. 创建session时用对应的provider_config

无需修改Client的创建逻辑