Slim subagent runtime surface and remove legacy interfaces

This commit is contained in:
LPF
2026-03-17 13:41:12 +08:00
parent 341e578c9f
commit 0674d85ae1
76 changed files with 778 additions and 8782 deletions

View File

@@ -234,7 +234,7 @@ func TestAIStudioChannelIDPrefersHealthyAvailableRelay(t *testing.T) {
}
providerRuntimeRegistry.mu.Unlock()
got := aistudioChannelID("aistudio", nil)
got := aistudioChannelCandidates("aistudio", nil)[0]
if got != "aistudio-good" {
t.Fatalf("expected aistudio-good, got %q", got)
}
@@ -249,7 +249,7 @@ func TestAIStudioChannelIDExplicitOptionWins(t *testing.T) {
}
providerRuntimeRegistry.mu.Unlock()
got := aistudioChannelID("aistudio", map[string]interface{}{"aistudio_channel": "manual"})
got := aistudioChannelCandidates("aistudio", map[string]interface{}{"aistudio_channel": "manual"})[0]
if got != "manual" {
t.Fatalf("expected explicit channel manual, got %q", got)
}
@@ -273,7 +273,7 @@ func TestAIStudioChannelIDPrefersMostRecentSuccessfulRelay(t *testing.T) {
aistudioRelayRegistry.succeeded["aistudio-b"] = time.Now()
aistudioRelayRegistry.mu.Unlock()
got := aistudioChannelID("aistudio", nil)
got := aistudioChannelCandidates("aistudio", nil)[0]
if got != "aistudio-b" {
t.Fatalf("expected most recent successful relay aistudio-b, got %q", got)
}

View File

@@ -35,14 +35,6 @@ func getAIStudioRelayManager() *wsrelay.Manager {
return aistudioRelayRegistry.manager
}
func aistudioChannelID(providerName string, options map[string]interface{}) string {
channels := aistudioChannelCandidates(providerName, options)
if len(channels) > 0 {
return channels[0]
}
return ""
}
func aistudioChannelCandidates(providerName string, options map[string]interface{}) []string {
for _, key := range []string{"aistudio_channel", "aistudio_provider", "relay_provider", "channel_id", "provider_id"} {
if value, ok := stringOption(options, key); ok && strings.TrimSpace(value) != "" {

View File

@@ -311,10 +311,6 @@ func (p *HTTPProvider) callResponses(ctx context.Context, messages []Message, to
return p.postJSON(ctx, endpointFor(p.apiBase, "/responses"), requestBody)
}
func toResponsesInputItems(msg Message) []map[string]interface{} {
return toResponsesInputItemsWithState(msg, nil)
}
func toResponsesInputItemsWithState(msg Message, pendingCalls map[string]struct{}) []map[string]interface{} {
role := strings.ToLower(strings.TrimSpace(msg.Role))
switch role {
@@ -2231,13 +2227,6 @@ func (p *HTTPProvider) codexCompatRequestBody(requestBody map[string]interface{}
return codexCompatRequestBody(requestBody)
}
func (p *HTTPProvider) useClaudeCompat() bool {
if p == nil || p.oauth == nil {
return false
}
return strings.EqualFold(strings.TrimSpace(p.oauth.cfg.Provider), defaultClaudeOAuthProvider)
}
func (p *HTTPProvider) oauthProvider() string {
if p == nil || p.oauth == nil {
return ""
@@ -2723,40 +2712,6 @@ func CreateProviderByName(cfg *config.Config, name string) (LLMProvider, error)
return NewHTTPProvider(routeName, pc.APIKey, pc.APIBase, defaultModel, pc.SupportsResponsesCompact, pc.Auth, time.Duration(pc.TimeoutSec)*time.Second, oauth), nil
}
func CreateProviders(cfg *config.Config) (map[string]LLMProvider, error) {
configs := getAllProviderConfigs(cfg)
if len(configs) == 0 {
return nil, fmt.Errorf("no providers configured")
}
out := make(map[string]LLMProvider, len(configs))
for name := range configs {
p, err := CreateProviderByName(cfg, name)
if err != nil {
return nil, err
}
out[name] = p
}
return out, nil
}
func GetProviderModels(cfg *config.Config, name string) []string {
pc, err := getProviderConfigByName(cfg, name)
if err != nil {
return nil
}
out := make([]string, 0, len(pc.Models))
seen := map[string]bool{}
for _, m := range pc.Models {
model := strings.TrimSpace(m)
if model == "" || seen[model] {
continue
}
seen[model] = true
out = append(out, model)
}
return out
}
func ProviderSupportsResponsesCompact(cfg *config.Config, name string) bool {
pc, err := getProviderConfigByName(cfg, name)
if err != nil {
@@ -2765,18 +2720,6 @@ func ProviderSupportsResponsesCompact(cfg *config.Config, name string) bool {
return pc.SupportsResponsesCompact
}
func ListProviderNames(cfg *config.Config) []string {
configs := getAllProviderConfigs(cfg)
if len(configs) == 0 {
return nil
}
names := make([]string, 0, len(configs))
for name := range configs {
names = append(names, name)
}
return names
}
func getAllProviderConfigs(cfg *config.Config) map[string]config.ProviderConfig {
return config.AllProviderConfigs(cfg)
}

View File

@@ -318,10 +318,6 @@ func (m *OAuthLoginManager) CredentialFile() string {
return m.manager.cfg.CredentialFile
}
func (m *OAuthLoginManager) StartManualFlow() (*OAuthPendingFlow, error) {
return m.StartManualFlowWithOptions(OAuthLoginOptions{})
}
func (m *OAuthLoginManager) StartManualFlowWithOptions(opts OAuthLoginOptions) (*OAuthPendingFlow, error) {
if m == nil || m.manager == nil {
return nil, fmt.Errorf("oauth login manager not configured")
@@ -351,10 +347,6 @@ func (m *OAuthLoginManager) StartManualFlowWithOptions(opts OAuthLoginOptions) (
}, nil
}
func (m *OAuthLoginManager) CompleteManualFlow(ctx context.Context, apiBase string, flow *OAuthPendingFlow, callbackURL string) (*OAuthSessionInfo, []string, error) {
return m.CompleteManualFlowWithOptions(ctx, apiBase, flow, callbackURL, OAuthLoginOptions{})
}
func (m *OAuthLoginManager) CompleteManualFlowWithOptions(ctx context.Context, apiBase string, flow *OAuthPendingFlow, callbackURL string, opts OAuthLoginOptions) (*OAuthSessionInfo, []string, error) {
if m == nil || m.manager == nil {
return nil, nil, fmt.Errorf("oauth login manager not configured")
@@ -392,10 +384,6 @@ func (m *OAuthLoginManager) CompleteManualFlowWithOptions(ctx context.Context, a
}, models, nil
}
func (m *OAuthLoginManager) ImportAuthJSON(ctx context.Context, apiBase string, fileName string, data []byte) (*OAuthSessionInfo, []string, error) {
return m.ImportAuthJSONWithOptions(ctx, apiBase, fileName, data, OAuthLoginOptions{})
}
func (m *OAuthLoginManager) ImportAuthJSONWithOptions(ctx context.Context, apiBase string, fileName string, data []byte, opts OAuthLoginOptions) (*OAuthSessionInfo, []string, error) {
if m == nil || m.manager == nil {
return nil, nil, fmt.Errorf("oauth login manager not configured")
@@ -746,47 +734,6 @@ func defaultRefreshLead(provider string, overrideSec int) time.Duration {
}
}
func (m *oauthManager) models(ctx context.Context, apiBase string) ([]string, error) {
attempts, err := m.prepareAttemptsLocked(ctx)
if err != nil {
return nil, err
}
if len(attempts) == 0 {
return nil, fmt.Errorf("oauth session not found, run `clawgo provider login` first")
}
var merged []string
seen := map[string]struct{}{}
var lastErr error
for _, attempt := range attempts {
client, err := m.httpClientForSession(attempt.Session)
if err != nil {
lastErr = err
continue
}
models, err := fetchOpenAIModels(ctx, client, apiBase, attempt.Token)
if err != nil {
lastErr = err
continue
}
attempt.Session.Models = append([]string(nil), models...)
_ = m.persistSessionLocked(attempt.Session)
for _, model := range models {
if _, ok := seen[model]; ok {
continue
}
seen[model] = struct{}{}
merged = append(merged, model)
}
}
if len(merged) > 0 {
return merged, nil
}
if lastErr != nil {
return nil, lastErr
}
return nil, fmt.Errorf("no oauth sessions available")
}
func (m *oauthManager) login(ctx context.Context, apiBase string, opts OAuthLoginOptions) (*oauthSession, []string, error) {
if m == nil {
return nil, nil, fmt.Errorf("oauth manager not configured")

View File

@@ -17,85 +17,6 @@ func openAICompatDefaultModel(base *HTTPProvider) string {
return base.GetDefaultModel()
}
func runQwenChat(ctx context.Context, base *HTTPProvider, messages []Message, tools []ToolDefinition, model string, options map[string]interface{}) (*LLMResponse, error) {
if base == nil {
return nil, fmt.Errorf("provider not configured")
}
requestBody := buildQwenChatRequest(base, messages, tools, model, options, false)
body, statusCode, contentType, err := doOpenAICompatJSONWithAttempts(ctx, base, "/chat/completions", requestBody, qwenProviderHooks{})
if err != nil {
return nil, err
}
if statusCode != http.StatusOK {
return nil, fmt.Errorf("API error (status %d, content-type %q): %s", statusCode, contentType, previewResponseBody(body))
}
if !json.Valid(body) {
return nil, fmt.Errorf("API error (status %d, content-type %q): non-JSON response: %s", statusCode, contentType, previewResponseBody(body))
}
return parseOpenAICompatResponse(body)
}
func runQwenChatStream(ctx context.Context, base *HTTPProvider, messages []Message, tools []ToolDefinition, model string, options map[string]interface{}, onDelta func(string)) (*LLMResponse, error) {
if base == nil {
return nil, fmt.Errorf("provider not configured")
}
if onDelta == nil {
onDelta = func(string) {}
}
requestBody := buildQwenChatRequest(base, messages, tools, model, options, true)
body, statusCode, contentType, err := doOpenAICompatStreamWithAttempts(ctx, base, "/chat/completions", requestBody, onDelta, qwenProviderHooks{})
if err != nil {
return nil, err
}
if statusCode != http.StatusOK {
return nil, fmt.Errorf("API error (status %d, content-type %q): %s", statusCode, contentType, previewResponseBody(body))
}
if !json.Valid(body) {
return nil, fmt.Errorf("API error (status %d, content-type %q): non-JSON response: %s", statusCode, contentType, previewResponseBody(body))
}
return parseOpenAICompatResponse(body)
}
func runOpenAICompatChat(ctx context.Context, base *HTTPProvider, messages []Message, tools []ToolDefinition, model string, options map[string]interface{}) (*LLMResponse, error) {
if base == nil {
return nil, fmt.Errorf("provider not configured")
}
body, statusCode, contentType, err := doOpenAICompatJSONWithAttempts(ctx, base, "/chat/completions", base.buildOpenAICompatChatRequest(messages, tools, model, options), nil)
if err != nil {
return nil, err
}
if statusCode != http.StatusOK {
return nil, fmt.Errorf("API error (status %d, content-type %q): %s", statusCode, contentType, previewResponseBody(body))
}
if !json.Valid(body) {
return nil, fmt.Errorf("API error (status %d, content-type %q): non-JSON response: %s", statusCode, contentType, previewResponseBody(body))
}
return parseOpenAICompatResponse(body)
}
func runOpenAICompatChatStream(ctx context.Context, base *HTTPProvider, messages []Message, tools []ToolDefinition, model string, options map[string]interface{}, onDelta func(string)) (*LLMResponse, error) {
if base == nil {
return nil, fmt.Errorf("provider not configured")
}
if onDelta == nil {
onDelta = func(string) {}
}
chatBody := base.buildOpenAICompatChatRequest(messages, tools, model, options)
chatBody["stream"] = true
chatBody["stream_options"] = map[string]interface{}{"include_usage": true}
body, statusCode, contentType, err := doOpenAICompatStreamWithAttempts(ctx, base, "/chat/completions", chatBody, onDelta, nil)
if err != nil {
return nil, err
}
if statusCode != http.StatusOK {
return nil, fmt.Errorf("API error (status %d, content-type %q): %s", statusCode, contentType, previewResponseBody(body))
}
if !json.Valid(body) {
return nil, fmt.Errorf("API error (status %d, content-type %q): non-JSON response: %s", statusCode, contentType, previewResponseBody(body))
}
return parseOpenAICompatResponse(body)
}
type openAICompatHooks interface {
beforeAttempt(attempt authAttempt) (int, []byte, string, bool)
endpoint(base *HTTPProvider, attempt authAttempt, path string) string