Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 11 additions & 0 deletions .env.template
Original file line number Diff line number Diff line change
Expand Up @@ -120,6 +120,17 @@
# fallback (default): use configured models only when upstream /models fails, is nil, or is empty.
# allowlist: expose only the configured models for providers that define a list, and skip their upstream /models calls.
# CONFIGURED_PROVIDER_MODELS_MODE=fallback

# --- Intelligent routing ---
# When a request does not pin a provider and multiple providers serve the same
# model, the gateway scores candidates and routes to the best one. Single-provider
# configs are unaffected (one candidate => no decision needed).
# Strategy: balanced (default), cost_only, latency_only, first_fit
# MODEL_ROUTING_STRATEGY=balanced
# Balanced weights as "cost_weight,latency_weight" (only affects balanced).
# MODEL_ROUTING_STRATEGY_WEIGHTS=0.6,0.4
# Filter candidates at/above this smoothed error rate [0,1] (default: 0.5).
# MODEL_ROUTING_MAX_ERROR_RATE=0.5
# Examples: OPENROUTER_MODELS=..., OPENROUTER_EU_MODELS=..., AZURE_MODELS=..., VLLM_MODELS=...

# Fallback & Workflow Configuration
Expand Down
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -37,3 +37,4 @@

# Superpower design docs and plans (never commit)
/docs/superpowers/
.superpowers/
10 changes: 10 additions & 0 deletions .idea/.gitignore

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

9 changes: 9 additions & 0 deletions .idea/GoModel.iml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

10 changes: 10 additions & 0 deletions .idea/go.imports.xml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

7 changes: 7 additions & 0 deletions .idea/golinter.xml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

8 changes: 8 additions & 0 deletions .idea/modules.xml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

6 changes: 6 additions & 0 deletions .idea/vcs.xml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -131,3 +131,4 @@ Full reference: `.env.template` and `config/config.yaml`
- **Guardrails:** Configured via `config/config.yaml` only (except `GUARDRAILS_ENABLED` env var)
- **Providers:** `OPENAI_API_KEY`, `ANTHROPIC_API_KEY`, `GEMINI_API_KEY`, `USE_GOOGLE_GEMINI_NATIVE_API` (true by default; false uses Gemini's OpenAI-compatible chat API), `XAI_API_KEY`, `GROQ_API_KEY`, `OPENROUTER_API_KEY`, `ZAI_API_KEY`, `ZAI_BASE_URL` (optional Z.ai endpoint override), `MINIMAX_API_KEY`, `MINIMAX_BASE_URL` (optional MiniMax endpoint override), `XIAOMI_API_KEY`, `XIAOMI_BASE_URL` (optional Xiaomi MiMo endpoint override), `OPENCODE_GO_API_KEY`, `OPENCODE_GO_BASE_URL` (optional OpenCode Go/Zen endpoint override; default `https://opencode.ai/zen/go/v1`), `OPENCODE_GO_MESSAGES_MODELS` (optional comma-separated model IDs routed to the Anthropic-native `/messages` endpoint instead of `/chat/completions`; default `qwen3.7-max`), `BAILIAN_API_KEY`, `BAILIAN_BASE_URL` (optional Bailian base URL for region switching; default `https://dashscope.aliyuncs.com/compatible-mode/v1`), `AZURE_API_KEY`, `AZURE_BASE_URL` (Azure OpenAI deployment base URL), `AZURE_API_VERSION` (optional Azure API version), `ORACLE_API_KEY` (Oracle API key), `ORACLE_BASE_URL` (Oracle OpenAI-compatible base URL), `<PROVIDER>[_SUFFIX]_MODELS` (comma-separated configured model list for any provider type), `OLLAMA_BASE_URL`, `VLLM_BASE_URL`, `VLLM_API_KEY` (optional upstream vLLM bearer token)
- **Provider model metadata:** `providers.<name>.models` accepts either model IDs (strings) or `{id, metadata}` objects. When `metadata` is supplied (`display_name`, `context_window`, `max_output_tokens`, `modes`, `capabilities`, `pricing`, …) it is merged onto the remote ai-model-list entry during enrichment, with operator values winning per-field. Primary use case: advertising context windows, capabilities, and pricing for local models (Ollama) and other custom endpoints whose IDs are not in the upstream registry.
- **Intelligent routing:** When a request does not pin a provider and multiple configured providers serve the same model ID, the gateway scores candidates and routes to the best one (single-provider configs are unaffected — one candidate means no decision). `MODEL_ROUTING_STRATEGY` (`balanced` default; also `cost_only`, `latency_only`, `first_fit`), `MODEL_ROUTING_STRATEGY_WEIGHTS` (`0.6,0.4` = cost_weight,latency_weight; balanced only), `MODEL_ROUTING_MAX_ERROR_RATE` (`0.5`; candidates at/above this smoothed error rate are filtered before scoring). Latency is tracked per provider as in-memory EWMA (alpha 0.1 p50 / 0.05 p99 / 0.2 error-rate), never persisted. Per-request overrides via request headers `X-GoModel-Routing-Strategy` and `X-GoModel-Routing-Weights` (`cost,latency`); invalid header values are silently dropped (WARN log) and fall back to the global strategy. Explicit provider-qualified requests (`openai-east/gpt-4o`) always bypass intelligent routing.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick | 🔵 Trivial | ⚡ Quick win

Split this into short sub-bullets for scanability.

Line 134 packs defaults, runtime behavior, EWMA internals, and header override semantics into one dense line. Breaking it into 4–6 bullets will make operator usage much easier to parse.

Suggested structure
-- **Intelligent routing:** When a request does not pin a provider and multiple configured providers serve the same model ID, ...
+- **Intelligent routing:** enabled when request has no pinned provider and multiple providers serve the same model.
+  - **Defaults:** `MODEL_ROUTING_STRATEGY=balanced`, `MODEL_ROUTING_STRATEGY_WEIGHTS=0.6,0.4`, `MODEL_ROUTING_MAX_ERROR_RATE=0.5`
+  - **Filtering:** candidates at/above `max_error_rate` are excluded before scoring.
+  - **Latency stats:** in-memory EWMA only (not persisted).
+  - **Per-request overrides:** `X-GoModel-Routing-Strategy`, `X-GoModel-Routing-Weights`; invalid values are ignored with WARN logs.
+  - **Bypass rule:** provider-qualified requests like `openai-east/gpt-4o` bypass intelligent routing.

As per coding guidelines, “Documentation should be concise, practical, and user-focused. Show defaults, explain when to change them, and include minimal examples when useful.”

📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
- **Intelligent routing:** When a request does not pin a provider and multiple configured providers serve the same model ID, the gateway scores candidates and routes to the best one (single-provider configs are unaffected — one candidate means no decision). `MODEL_ROUTING_STRATEGY` (`balanced` default; also `cost_only`, `latency_only`, `first_fit`), `MODEL_ROUTING_STRATEGY_WEIGHTS` (`0.6,0.4` = cost_weight,latency_weight; balanced only), `MODEL_ROUTING_MAX_ERROR_RATE` (`0.5`; candidates at/above this smoothed error rate are filtered before scoring). Latency is tracked per provider as in-memory EWMA (alpha 0.1 p50 / 0.05 p99 / 0.2 error-rate), never persisted. Per-request overrides via request headers `X-GoModel-Routing-Strategy` and `X-GoModel-Routing-Weights` (`cost,latency`); invalid header values are silently dropped (WARN log) and fall back to the global strategy. Explicit provider-qualified requests (`openai-east/gpt-4o`) always bypass intelligent routing.
- **Intelligent routing:** enabled when request has no pinned provider and multiple providers serve the same model.
- **Defaults:** `MODEL_ROUTING_STRATEGY=balanced`, `MODEL_ROUTING_STRATEGY_WEIGHTS=0.6,0.4`, `MODEL_ROUTING_MAX_ERROR_RATE=0.5`
- **Filtering:** candidates at/above `max_error_rate` are excluded before scoring.
- **Latency stats:** in-memory EWMA only (not persisted).
- **Per-request overrides:** `X-GoModel-Routing-Strategy`, `X-GoModel-Routing-Weights`; invalid values are ignored with WARN logs.
- **Bypass rule:** provider-qualified requests like `openai-east/gpt-4o` bypass intelligent routing.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@CLAUDE.md` at line 134, The "Intelligent routing" documentation section on
line 134 is a single overly dense bullet containing multiple distinct concepts
(routing strategy defaults, configuration options, EWMA tracking details, header
overrides, and explicit provider behavior). Break this into 4-6 focused
sub-bullets, each addressing one specific topic: one for the routing strategy
configuration and defaults, one for the scoring weights, one for error rate
filtering, one for EWMA latency tracking mechanics, one for request header
overrides, and one for explicit provider-qualified request bypass behavior. This
will improve scannability and make it easier for operators to find and
understand each configuration option independently.

Source: Coding guidelines

35 changes: 35 additions & 0 deletions c.out
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
mode: set
gomodel/internal/providers/bailian/bailian.go:39.86,48.2 2 1
gomodel/internal/providers/bailian/bailian.go:52.97,60.2 1 1
gomodel/internal/providers/bailian/bailian.go:62.51,64.2 1 1
gomodel/internal/providers/bailian/bailian.go:67.43,69.2 1 1
gomodel/internal/providers/bailian/bailian.go:73.107,75.2 1 1
gomodel/internal/providers/bailian/bailian.go:78.108,80.2 1 1
gomodel/internal/providers/bailian/bailian.go:83.82,85.2 1 1
gomodel/internal/providers/bailian/bailian.go:88.112,90.2 1 1
gomodel/internal/providers/bailian/bailian.go:93.108,95.2 1 1
gomodel/internal/providers/bailian/bailian.go:101.113,103.2 1 1
gomodel/internal/providers/bailian/bailian.go:106.118,108.2 1 1
gomodel/internal/providers/bailian/bailian.go:111.106,113.2 1 1
gomodel/internal/providers/bailian/bailian.go:116.90,118.2 1 1
gomodel/internal/providers/bailian/bailian.go:121.111,123.2 1 1
gomodel/internal/providers/bailian/bailian.go:126.93,128.2 1 1
gomodel/internal/providers/bailian/bailian.go:131.104,133.2 1 1
gomodel/internal/providers/bailian/bailian.go:136.107,138.16 2 1
gomodel/internal/providers/bailian/bailian.go:138.16,140.3 1 0
gomodel/internal/providers/bailian/bailian.go:141.2,142.18 2 1
gomodel/internal/providers/bailian/bailian.go:146.124,148.16 2 1
gomodel/internal/providers/bailian/bailian.go:148.16,150.3 1 0
gomodel/internal/providers/bailian/bailian.go:151.2,151.27 1 1
gomodel/internal/providers/bailian/bailian.go:151.27,153.3 1 0
gomodel/internal/providers/bailian/bailian.go:154.2,154.18 1 1
gomodel/internal/providers/bailian/bailian.go:158.86,160.16 2 1
gomodel/internal/providers/bailian/bailian.go:160.16,162.3 1 0
gomodel/internal/providers/bailian/bailian.go:163.2,164.18 2 1
gomodel/internal/providers/bailian/bailian.go:168.97,170.2 1 1
gomodel/internal/providers/bailian/bailian.go:173.102,175.2 1 1
gomodel/internal/providers/bailian/bailian.go:189.67,190.40 1 1
gomodel/internal/providers/bailian/bailian.go:190.40,192.3 1 1
gomodel/internal/providers/bailian/bailian.go:194.2,194.82 1 1
gomodel/internal/providers/bailian/bailian.go:194.82,198.3 3 1
gomodel/internal/providers/bailian/bailian.go:199.2,206.16 6 1
12 changes: 12 additions & 0 deletions config/config.example.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -356,3 +356,15 @@ providers:
# input_per_mtok: 0
# output_per_mtok: 0
# - Gemma4-31B

# --- Intelligent routing ---
# When a request does not pin a provider and multiple providers serve the same
# model, the gateway scores candidates and routes to the best one. Single-provider
# configs are unaffected (one candidate means no decision needed).
# Default: strategy=balanced, weights={cost:0.6 latency:0.4}, max_error_rate=0.5
# router:
# strategy: balanced # balanced, cost_only, latency_only, first_fit
# weights:
# cost: 0.6
# latency: 0.4
# max_error_rate: 0.5
6 changes: 6 additions & 0 deletions config/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ type Config struct {
Fallback FallbackConfig `yaml:"fallback"`
Workflows WorkflowsConfig `yaml:"workflows"`
Resilience ResilienceConfig `yaml:"resilience"`
Router RouterConfig `yaml:"router"`
}

// LoadResult is returned by Load and bundles the application config with the raw
Expand Down Expand Up @@ -120,6 +121,7 @@ func buildDefaultConfig() *Config {
Retry: DefaultRetryConfig(),
CircuitBreaker: DefaultCircuitBreakerConfig(),
},
Router: DefaultRouterConfig(),
Admin: AdminConfig{
EndpointsEnabled: true,
UIEnabled: true,
Expand Down Expand Up @@ -194,6 +196,10 @@ func Load() (*LoadResult, error) {
return nil, err
}

if err := ValidateRouterConfig(&cfg.Router); err != nil {
return nil, err
}

return &LoadResult{
Config: cfg,
RawProviders: rawProviders,
Expand Down
127 changes: 127 additions & 0 deletions config/router.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,127 @@
package config

import (
"fmt"
"math"
"strconv"
"strings"
)

// RouterConfig configures intelligent provider selection. When a request does
// not pin a provider and multiple providers serve the same model, the gateway
// scores candidates by cost and/or latency using the configured strategy.
type RouterConfig struct {
// Strategy is the default strategy id: "balanced", "cost_only",
// "latency_only", or "first_fit". Empty defaults to "balanced".
Strategy string `yaml:"strategy" json:"strategy" env:"MODEL_ROUTING_STRATEGY"`

// Weights tunes the balanced strategy. CostWeight and LatencyWeight are
// ignored by other strategies.
Weights RouterWeights `yaml:"weights" json:"weights"`

// WeightsCSV is the env-only form of Weights as "cost,latency"
// (e.g. "0.6,0.4"). Parsed into Weights during validation.
WeightsCSV string `yaml:"-" json:"-" env:"MODEL_ROUTING_STRATEGY_WEIGHTS"`

// MaxErrorRate filters candidates at/above this smoothed error ratio before
// scoring. Zero falls back to the strategy default (0.5).
MaxErrorRate float64 `yaml:"max_error_rate" json:"max_error_rate" env:"MODEL_ROUTING_MAX_ERROR_RATE"`
}

// RouterWeights tunes the balanced strategy's cost/latency trade-off.
type RouterWeights struct {
Cost float64 `yaml:"cost" json:"cost" env:"MODEL_ROUTING_COST_WEIGHT"`
Latency float64 `yaml:"latency" json:"latency" env:"MODEL_ROUTING_LATENCY_WEIGHT"`
}

// RouterStrategyBalanced and the other built-in strategy ids.
const (
RouterStrategyBalanced = "balanced"
RouterStrategyCostOnly = "cost_only"
RouterStrategyLatencyOnly = "latency_only"
RouterStrategyFirstFit = "first_fit"
)

// DefaultRouterConfig returns the default router configuration: balanced strategy
// with 0.6 cost / 0.4 latency weights and a 0.5 max error rate.
func DefaultRouterConfig() RouterConfig {
return RouterConfig{
Strategy: RouterStrategyBalanced,
Weights: RouterWeights{
Cost: 0.6,
Latency: 0.4,
},
MaxErrorRate: 0.5,
}
}

// ValidateRouterConfig normalizes and validates the router config, applying
// defaults for empty/invalid fields.
func ValidateRouterConfig(cfg *RouterConfig) error {
if cfg.Strategy == "" {
cfg.Strategy = RouterStrategyBalanced
}
strategy := strings.ToLower(strings.TrimSpace(cfg.Strategy))
switch strategy {
Comment thread
coderabbitai[bot] marked this conversation as resolved.
case RouterStrategyBalanced, RouterStrategyCostOnly, RouterStrategyLatencyOnly, RouterStrategyFirstFit:
default:
return fmt.Errorf("invalid router.strategy %q: must be one of balanced, cost_only, latency_only, first_fit", cfg.Strategy)
}
cfg.Strategy = strategy

// MODEL_ROUTING_STRATEGY_WEIGHTS overrides YAML weights when set.
if strings.TrimSpace(cfg.WeightsCSV) != "" {
cost, lat, err := parseWeightsCSV(cfg.WeightsCSV)
if err != nil {
return fmt.Errorf("invalid MODEL_ROUTING_STRATEGY_WEIGHTS: %w", err)
}
cfg.Weights = RouterWeights{Cost: cost, Latency: lat}
}

if math.IsNaN(cfg.Weights.Cost) || math.IsNaN(cfg.Weights.Latency) ||
math.IsInf(cfg.Weights.Cost, 0) || math.IsInf(cfg.Weights.Latency, 0) {
return fmt.Errorf("router.weights must be finite numbers, got cost=%v latency=%v", cfg.Weights.Cost, cfg.Weights.Latency)
}
if cfg.Weights.Cost < 0 || cfg.Weights.Latency < 0 {
return fmt.Errorf("router.weights must be non-negative, got cost=%v latency=%v", cfg.Weights.Cost, cfg.Weights.Latency)
}
if cfg.Weights.Cost == 0 && cfg.Weights.Latency == 0 && strategy == RouterStrategyBalanced {
cfg.Weights = RouterWeights{Cost: 0.6, Latency: 0.4}
}
if math.IsNaN(cfg.MaxErrorRate) || math.IsInf(cfg.MaxErrorRate, 0) {
return fmt.Errorf("router.max_error_rate must be a finite number, got %v", cfg.MaxErrorRate)
}
if cfg.MaxErrorRate < 0 || cfg.MaxErrorRate > 1 {
return fmt.Errorf("router.max_error_rate must be in [0, 1], got %v", cfg.MaxErrorRate)
}
Comment on lines +94 to +96

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Zero threshold ignored

The config validator accepts router.max_error_rate: 0 as a valid value, but each strategy later treats MaxErrorRate == 0 as “use the default 0.5”. An operator who configures zero to block any provider with a nonzero smoothed error rate will silently get the default threshold instead, so providers with error rates below 0.5 can still receive traffic.

Context Used: CLAUDE.md (source)

Comment thread
coderabbitai[bot] marked this conversation as resolved.
return nil
}

// parseWeightsCSV parses a "cost,latency" string into two non-negative floats.
func parseWeightsCSV(s string) (cost, latency float64, err error) {
parts := strings.Split(s, ",")
if len(parts) != 2 {
return 0, 0, fmt.Errorf("expected two comma-separated weights, got %q", s)
}
if cost, err = parseFloatField(parts[0], "cost"); err != nil {
return 0, 0, err
}
if latency, err = parseFloatField(parts[1], "latency"); err != nil {
return 0, 0, err
}
if cost < 0 || latency < 0 {
return 0, 0, fmt.Errorf("weights must be non-negative, got %v,%v", cost, latency)
}
return cost, latency, nil
}

func parseFloatField(s, name string) (float64, error) {
v, err := strconv.ParseFloat(strings.TrimSpace(s), 64)
if err != nil {
return 0, fmt.Errorf("invalid %s weight %q: %w", name, s, err)
}
if math.IsNaN(v) || math.IsInf(v, 0) {
return 0, fmt.Errorf("invalid %s weight %q: must be a finite number", name, s)
}
return v, nil
}
35 changes: 35 additions & 0 deletions coverage-bailian
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
mode: set
gomodel/internal/providers/bailian/bailian.go:39.86,48.2 2 1
gomodel/internal/providers/bailian/bailian.go:52.97,60.2 1 1
gomodel/internal/providers/bailian/bailian.go:62.51,64.2 1 1
gomodel/internal/providers/bailian/bailian.go:67.43,69.2 1 1
gomodel/internal/providers/bailian/bailian.go:73.107,75.2 1 1
gomodel/internal/providers/bailian/bailian.go:78.108,80.2 1 1
gomodel/internal/providers/bailian/bailian.go:83.82,85.2 1 1
gomodel/internal/providers/bailian/bailian.go:88.112,90.2 1 1
gomodel/internal/providers/bailian/bailian.go:93.108,95.2 1 1
gomodel/internal/providers/bailian/bailian.go:101.113,103.2 1 1
gomodel/internal/providers/bailian/bailian.go:106.118,108.2 1 1
gomodel/internal/providers/bailian/bailian.go:111.106,113.2 1 0
gomodel/internal/providers/bailian/bailian.go:116.90,118.2 1 0
gomodel/internal/providers/bailian/bailian.go:121.111,123.2 1 0
gomodel/internal/providers/bailian/bailian.go:126.93,128.2 1 0
gomodel/internal/providers/bailian/bailian.go:131.104,133.2 1 0
gomodel/internal/providers/bailian/bailian.go:136.107,138.16 2 0
gomodel/internal/providers/bailian/bailian.go:138.16,140.3 1 0
gomodel/internal/providers/bailian/bailian.go:141.2,142.18 2 0
gomodel/internal/providers/bailian/bailian.go:146.124,148.16 2 0
gomodel/internal/providers/bailian/bailian.go:148.16,150.3 1 0
gomodel/internal/providers/bailian/bailian.go:151.2,151.27 1 0
gomodel/internal/providers/bailian/bailian.go:151.27,153.3 1 0
gomodel/internal/providers/bailian/bailian.go:154.2,154.18 1 0
gomodel/internal/providers/bailian/bailian.go:158.86,160.16 2 0
gomodel/internal/providers/bailian/bailian.go:160.16,162.3 1 0
gomodel/internal/providers/bailian/bailian.go:163.2,164.18 2 0
gomodel/internal/providers/bailian/bailian.go:168.97,170.2 1 0
gomodel/internal/providers/bailian/bailian.go:173.102,175.2 1 0
gomodel/internal/providers/bailian/bailian.go:189.67,190.40 1 1
gomodel/internal/providers/bailian/bailian.go:190.40,192.3 1 1
gomodel/internal/providers/bailian/bailian.go:194.2,194.82 1 1
gomodel/internal/providers/bailian/bailian.go:194.82,198.3 3 1
gomodel/internal/providers/bailian/bailian.go:199.2,206.16 6 1
35 changes: 35 additions & 0 deletions coverage-bailian.out
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
mode: set
gomodel/internal/providers/bailian/bailian.go:39.86,48.2 2 1
gomodel/internal/providers/bailian/bailian.go:52.97,60.2 1 1
gomodel/internal/providers/bailian/bailian.go:62.51,64.2 1 1
gomodel/internal/providers/bailian/bailian.go:67.43,69.2 1 1
gomodel/internal/providers/bailian/bailian.go:73.107,75.2 1 1
gomodel/internal/providers/bailian/bailian.go:78.108,80.2 1 1
gomodel/internal/providers/bailian/bailian.go:83.82,85.2 1 1
gomodel/internal/providers/bailian/bailian.go:88.112,90.2 1 1
gomodel/internal/providers/bailian/bailian.go:93.108,95.2 1 1
gomodel/internal/providers/bailian/bailian.go:101.113,103.2 1 1
gomodel/internal/providers/bailian/bailian.go:106.118,108.2 1 1
gomodel/internal/providers/bailian/bailian.go:111.106,113.2 1 0
gomodel/internal/providers/bailian/bailian.go:116.90,118.2 1 0
gomodel/internal/providers/bailian/bailian.go:121.111,123.2 1 0
gomodel/internal/providers/bailian/bailian.go:126.93,128.2 1 0
gomodel/internal/providers/bailian/bailian.go:131.104,133.2 1 0
gomodel/internal/providers/bailian/bailian.go:136.107,138.16 2 0
gomodel/internal/providers/bailian/bailian.go:138.16,140.3 1 0
gomodel/internal/providers/bailian/bailian.go:141.2,142.18 2 0
gomodel/internal/providers/bailian/bailian.go:146.124,148.16 2 0
gomodel/internal/providers/bailian/bailian.go:148.16,150.3 1 0
gomodel/internal/providers/bailian/bailian.go:151.2,151.27 1 0
gomodel/internal/providers/bailian/bailian.go:151.27,153.3 1 0
gomodel/internal/providers/bailian/bailian.go:154.2,154.18 1 0
gomodel/internal/providers/bailian/bailian.go:158.86,160.16 2 0
gomodel/internal/providers/bailian/bailian.go:160.16,162.3 1 0
gomodel/internal/providers/bailian/bailian.go:163.2,164.18 2 0
gomodel/internal/providers/bailian/bailian.go:168.97,170.2 1 0
gomodel/internal/providers/bailian/bailian.go:173.102,175.2 1 0
gomodel/internal/providers/bailian/bailian.go:189.67,190.40 1 1
gomodel/internal/providers/bailian/bailian.go:190.40,192.3 1 1
gomodel/internal/providers/bailian/bailian.go:194.2,194.82 1 1
gomodel/internal/providers/bailian/bailian.go:194.82,198.3 3 1
gomodel/internal/providers/bailian/bailian.go:199.2,206.16 6 1
Binary file added gomodel.exe
Binary file not shown.
Binary file added gomodel.exe~
Binary file not shown.
Loading