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
2 changes: 2 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -329,6 +329,7 @@ Config file: `~/.opencodereview/config.json`
| `llm.auth_token` | string | `sk-xxxxxxx` |
| `llm.model` | string | `claude-opus-4-6` |
| `llm.use_anthropic` | boolean | `true` \| `false` |
| `llm.use_max_completion_tokens` | boolean | `true` = use `max_completion_tokens`, `false` = use `max_tokens` (default) |
| `language` | string | `English` \| `Chinese` (default: Chinese) |
| `telemetry.enabled` | boolean | `true` \| `false` |
| `telemetry.exporter` | string | `console` \| `otlp` |
Expand All @@ -345,6 +346,7 @@ Environment variables take precedence over the config file.
| `OCR_LLM_TOKEN` | API key / auth token |
| `OCR_LLM_MODEL` | Model name |
| `OCR_USE_ANTHROPIC` | `true` = Anthropic, `false` = OpenAI |
| `OCR_USE_MAX_COMPLETION_TOKENS` | `true` = use `max_completion_tokens` param instead of `max_tokens` |


## Telemetry
Expand Down
2 changes: 2 additions & 0 deletions README.zh-CN.md
Original file line number Diff line number Diff line change
Expand Up @@ -319,6 +319,7 @@ OCR 通过四层优先级链解析评审规则。每层采用首次匹配原则
| `llm.auth_token` | string | `sk-xxxxxxx` |
| `llm.model` | string | `claude-opus-4-6` |
| `llm.use_anthropic` | boolean | `true` \| `false` |
| `llm.use_max_completion_tokens` | boolean | `true` = 使用 `max_completion_tokens`,`false` = 使用 `max_tokens`(默认) |
| `language` | string | `English` \| `Chinese`(默认:Chinese) |
| `telemetry.enabled` | boolean | `true` \| `false` |
| `telemetry.exporter` | string | `console` \| `otlp` |
Expand All @@ -335,6 +336,7 @@ OCR 通过四层优先级链解析评审规则。每层采用首次匹配原则
| `OCR_LLM_TOKEN` | API 密钥 / 认证令牌 |
| `OCR_LLM_MODEL` | 模型名称 |
| `OCR_USE_ANTHROPIC` | `true` = Anthropic,`false` = OpenAI |
| `OCR_USE_MAX_COMPLETION_TOKENS` | `true` = 使用 `max_completion_tokens` 参数代替 `max_tokens` |


## 遥测
Expand Down
19 changes: 13 additions & 6 deletions cmd/opencodereview/config_cmd.go
Original file line number Diff line number Diff line change
Expand Up @@ -77,11 +77,12 @@ type Config struct {
}

type LlmConfig struct {
URL string `json:"url,omitempty"`
AuthToken string `json:"auth_token,omitempty"`
Model string `json:"model,omitempty"`
UseAnthropic *bool `json:"use_anthropic,omitempty"` // nil = default true; false = OpenAI protocol
ExtraBody map[string]any `json:"extra_body,omitempty"`
URL string `json:"url,omitempty"`
AuthToken string `json:"auth_token,omitempty"`
Model string `json:"model,omitempty"`
UseAnthropic *bool `json:"use_anthropic,omitempty"` // nil = default true; false = OpenAI protocol
UseMaxCompletionTokens *bool `json:"use_max_completion_tokens,omitempty"` // nil = default false; true = use max_completion_tokens
ExtraBody map[string]any `json:"extra_body,omitempty"`
}

// TelemetryConfig holds telemetry-specific settings.
Expand Down Expand Up @@ -137,6 +138,12 @@ func setConfigValue(cfg *Config, key, value string) error {
return fmt.Errorf("invalid boolean for llm.use_anthropic: %w", err)
}
cfg.Llm.UseAnthropic = &b
case "llm.use_max_completion_tokens", "llm.UseMaxCompletionTokens":
b, err := strconv.ParseBool(value)
if err != nil {
return fmt.Errorf("invalid boolean for llm.use_max_completion_tokens: %w", err)
}
cfg.Llm.UseMaxCompletionTokens = &b
case "language", "Language":
cfg.Language = value
case "telemetry.enabled", "telemetry.Enabled":
Expand Down Expand Up @@ -166,7 +173,7 @@ func setConfigValue(cfg *Config, key, value string) error {
}
cfg.Llm.ExtraBody = m
default:
return fmt.Errorf("unknown config key: %s\nSupported keys: llm.url, llm.auth_token, llm.model, llm.use_anthropic, llm.extra_body, language, telemetry.enabled, telemetry.exporter, telemetry.otlp_endpoint, telemetry.content_logging", key)
return fmt.Errorf("unknown config key: %s\nSupported keys: llm.url, llm.auth_token, llm.model, llm.use_anthropic, llm.use_max_completion_tokens, llm.extra_body, language, telemetry.enabled, telemetry.exporter, telemetry.otlp_endpoint, telemetry.content_logging", key)
}
return nil
}
Expand Down
146 changes: 146 additions & 0 deletions cmd/opencodereview/config_cmd_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,146 @@
package main

import (
"testing"
)

func boolVal(b bool) *bool { return &b }

func TestSetConfigValue_StringKeys(t *testing.T) {
tests := []struct {
key string
value string
checkFn func(*Config) string
}{
{"llm.url", "https://api.example.com/v1", func(c *Config) string { return c.Llm.URL }},
{"llm.URL", "https://api.example.com/v2", func(c *Config) string { return c.Llm.URL }},
{"llm.auth_token", "sk-token-123", func(c *Config) string { return c.Llm.AuthToken }},
{"llm.AuthToken", "sk-token-456", func(c *Config) string { return c.Llm.AuthToken }},
{"llm.model", "gpt-4", func(c *Config) string { return c.Llm.Model }},
{"llm.Model", "claude-opus-4-6", func(c *Config) string { return c.Llm.Model }},
{"language", "English", func(c *Config) string { return c.Language }},
{"Language", "Chinese", func(c *Config) string { return c.Language }},
{"telemetry.exporter", "otlp", func(c *Config) string { return c.Telemetry.Exporter }},
{"telemetry.Exporter", "console", func(c *Config) string { return c.Telemetry.Exporter }},
{"telemetry.otlp_endpoint", "localhost:4317", func(c *Config) string { return c.Telemetry.OTLPEndpoint }},
{"telemetry.OTLPEndpoint", "collector:4317", func(c *Config) string { return c.Telemetry.OTLPEndpoint }},
}

for _, tt := range tests {
t.Run(tt.key+"="+tt.value, func(t *testing.T) {
cfg := &Config{}
if err := setConfigValue(cfg, tt.key, tt.value); err != nil {
t.Fatalf("unexpected error: %v", err)
}
got := tt.checkFn(cfg)
if got != tt.value {
t.Errorf("got %q, want %q", got, tt.value)
}
})
}
}

func TestSetConfigValue_BoolKeys(t *testing.T) {
tests := []struct {
key string
value string
want bool
checkFn func(*Config) bool
}{
{"llm.use_anthropic", "true", true, func(c *Config) bool { return *c.Llm.UseAnthropic }},
{"llm.UseAnthropic", "false", false, func(c *Config) bool { return *c.Llm.UseAnthropic }},
{"llm.use_max_completion_tokens", "true", true, func(c *Config) bool { return *c.Llm.UseMaxCompletionTokens }},
{"llm.UseMaxCompletionTokens", "false", false, func(c *Config) bool { return *c.Llm.UseMaxCompletionTokens }},
{"telemetry.enabled", "true", true, func(c *Config) bool { return c.Telemetry.Enabled }},
{"telemetry.Enabled", "false", false, func(c *Config) bool { return c.Telemetry.Enabled }},
{"telemetry.content_logging", "true", true, func(c *Config) bool { return c.Telemetry.ContentLog }},
{"telemetry.ContentLog", "false", false, func(c *Config) bool { return c.Telemetry.ContentLog }},
}

for _, tt := range tests {
t.Run(tt.key+"="+tt.value, func(t *testing.T) {
cfg := &Config{}
if err := setConfigValue(cfg, tt.key, tt.value); err != nil {
t.Fatalf("unexpected error: %v", err)
}
got := tt.checkFn(cfg)
if got != tt.want {
t.Errorf("got %v, want %v", got, tt.want)
}
})
}
}

func TestSetConfigValue_BoolKeys_InvalidValue(t *testing.T) {
keys := []string{
"llm.use_anthropic",
"llm.use_max_completion_tokens",
"telemetry.enabled",
"telemetry.content_logging",
}

for _, key := range keys {
t.Run(key, func(t *testing.T) {
cfg := &Config{}
err := setConfigValue(cfg, key, "not-a-bool")
if err == nil {
t.Fatal("expected error for invalid boolean value")
}
})
}
}

func TestSetConfigValue_ExtraBody(t *testing.T) {
tests := []struct {
name string
key string
value string
wantErr bool
}{
{"valid JSON", "llm.extra_body", `{"thinking":{"type":"disabled"}}`, false},
{"alias key", "llm.ExtraBody", `{"key":"value"}`, false},
{"invalid JSON", "llm.extra_body", `not json`, true},
}

for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
cfg := &Config{}
err := setConfigValue(cfg, tt.key, tt.value)
if tt.wantErr {
if err == nil {
t.Fatal("expected error for invalid JSON")
}
} else {
if err != nil {
t.Fatalf("unexpected error: %v", err)
}
if cfg.Llm.ExtraBody == nil {
t.Fatal("expected non-nil ExtraBody")
}
}
})
}
}

func TestSetConfigValue_UnknownKey(t *testing.T) {
cfg := &Config{}
err := setConfigValue(cfg, "unknown.key", "value")
if err == nil {
t.Fatal("expected error for unknown key")
}
}

func TestSetConfigValue_TelemetryInitialized(t *testing.T) {
cfg := &Config{}
if cfg.Telemetry != nil {
t.Fatal("telemetry should be nil initially")
}

if err := setConfigValue(cfg, "telemetry.enabled", "true"); err != nil {
t.Fatalf("unexpected error: %v", err)
}

if cfg.Telemetry == nil {
t.Fatal("telemetry should be initialized after setting telemetry key")
}
}
2 changes: 1 addition & 1 deletion cmd/opencodereview/flags.go
Original file line number Diff line number Diff line change
Expand Up @@ -254,5 +254,5 @@ Examples:
ocr config set language English
ocr config set telemetry.enabled true

Supported keys: llm.url, llm.auth_token, llm.model, llm.use_anthropic, llm.extra_body, language, telemetry.enabled, telemetry.exporter, telemetry.otlp_endpoint, telemetry.content_logging`)
Supported keys: llm.url, llm.auth_token, llm.model, llm.use_anthropic, llm.use_max_completion_tokens, llm.extra_body, language, telemetry.enabled, telemetry.exporter, telemetry.otlp_endpoint, telemetry.content_logging`)
}
14 changes: 14 additions & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,9 @@ module github.com/open-code-review/open-code-review
go 1.25.0

require (
github.com/anthropics/anthropic-sdk-go v1.46.0
github.com/bmatcuk/doublestar/v4 v4.10.0
github.com/openai/openai-go/v3 v3.38.0
github.com/pkoukk/tiktoken-go v0.1.8
go.opentelemetry.io/otel v1.43.0
go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc v1.43.0
Expand All @@ -17,21 +19,33 @@ require (
)

require (
github.com/bahlo/generic-list-go v0.2.0 // indirect
github.com/buger/jsonparser v1.1.2 // indirect
github.com/cenkalti/backoff/v5 v5.0.3 // indirect
github.com/cespare/xxhash/v2 v2.3.0 // indirect
github.com/dlclark/regexp2 v1.10.0 // indirect
github.com/go-logr/logr v1.4.3 // indirect
github.com/go-logr/stdr v1.2.2 // indirect
github.com/google/uuid v1.6.0 // indirect
github.com/grpc-ecosystem/grpc-gateway/v2 v2.28.0 // indirect
github.com/invopop/jsonschema v0.13.0 // indirect
github.com/mailru/easyjson v0.7.7 // indirect
github.com/standard-webhooks/standard-webhooks/libraries v0.0.1 // indirect
github.com/tidwall/gjson v1.18.0 // indirect
github.com/tidwall/match v1.1.1 // indirect
github.com/tidwall/pretty v1.2.1 // indirect
github.com/tidwall/sjson v1.2.5 // indirect
github.com/wk8/go-ordered-map/v2 v2.1.8 // indirect
go.opentelemetry.io/auto/sdk v1.2.1 // indirect
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.43.0 // indirect
go.opentelemetry.io/proto/otlp v1.10.0 // indirect
golang.org/x/net v0.52.0 // indirect
golang.org/x/sync v0.20.0 // indirect
golang.org/x/sys v0.42.0 // indirect
golang.org/x/text v0.35.0 // indirect
google.golang.org/genproto/googleapis/api v0.0.0-20260401024825-9d38bb4040a9 // indirect
google.golang.org/genproto/googleapis/rpc v0.0.0-20260401024825-9d38bb4040a9 // indirect
google.golang.org/grpc v1.80.0 // indirect
google.golang.org/protobuf v1.36.11 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
)
42 changes: 42 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
@@ -1,5 +1,11 @@
github.com/anthropics/anthropic-sdk-go v1.46.0 h1:yl3n+el5ZfNgiCtQ7zQ7s/NXxB11YbrKXdc3uLPNWlU=
github.com/anthropics/anthropic-sdk-go v1.46.0/go.mod h1:bx5vWuHFuGPkELH8Z4KUiNSohFnUwScdpTyr+50myPo=
github.com/bahlo/generic-list-go v0.2.0 h1:5sz/EEAK+ls5wF+NeqDpk5+iNdMDXrh3z3nPnH1Wvgk=
github.com/bahlo/generic-list-go v0.2.0/go.mod h1:2KvAjgMlE5NNynlg/5iLrrCCZ2+5xWbdbCW3pNTGyYg=
github.com/bmatcuk/doublestar/v4 v4.10.0 h1:zU9WiOla1YA122oLM6i4EXvGW62DvKZVxIe6TYWexEs=
github.com/bmatcuk/doublestar/v4 v4.10.0/go.mod h1:xBQ8jztBU6kakFMg+8WGxn0c6z1fTSPVIjEY1Wr7jzc=
github.com/buger/jsonparser v1.1.2 h1:frqHqw7otoVbk5M8LlE/L7HTnIq2v9RX6EJ48i9AxJk=
github.com/buger/jsonparser v1.1.2/go.mod h1:6RYKKt7H4d4+iWqouImQ9R2FZql3VbhNgx27UK13J/0=
github.com/cenkalti/backoff/v5 v5.0.3 h1:ZN+IMa753KfX5hd8vVaMixjnqRZ3y8CuJKRKj1xcsSM=
github.com/cenkalti/backoff/v5 v5.0.3/go.mod h1:rkhZdG3JZukswDf7f0cwqPNk4K0sa+F97BxZthm/crw=
github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs=
Expand All @@ -8,6 +14,8 @@ github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/dlclark/regexp2 v1.10.0 h1:+/GIL799phkJqYW+3YbOd8LCcbHzT0Pbo8zl70MHsq0=
github.com/dlclark/regexp2 v1.10.0/go.mod h1:DHkYz0B9wPfa6wondMfaivmHpzrQ3v9q8cnmRbL6yW8=
github.com/dnaeon/go-vcr v1.2.0 h1:zHCHvJYTMh1N7xnV7zf1m1GPBF9Ad0Jk/whtQ1663qI=
github.com/dnaeon/go-vcr v1.2.0/go.mod h1:R4UdLID7HZT3taECzJs4YgbbH6PIGXB6W/sc5OLb6RQ=
github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
github.com/go-logr/logr v1.4.3 h1:CjnDlHq8ikf6E492q6eKboGOC0T8CDaOvkHCIg8idEI=
github.com/go-logr/logr v1.4.3/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY=
Expand All @@ -21,12 +29,39 @@ github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/grpc-ecosystem/grpc-gateway/v2 v2.28.0 h1:HWRh5R2+9EifMyIHV7ZV+MIZqgz+PMpZ14Jynv3O2Zs=
github.com/grpc-ecosystem/grpc-gateway/v2 v2.28.0/go.mod h1:JfhWUomR1baixubs02l85lZYYOm7LV6om4ceouMv45c=
github.com/invopop/jsonschema v0.13.0 h1:KvpoAJWEjR3uD9Kbm2HWJmqsEaHt8lBUpd0qHcIi21E=
github.com/invopop/jsonschema v0.13.0/go.mod h1:ffZ5Km5SWWRAIN6wbDXItl95euhFz2uON45H2qjYt+0=
github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y=
github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
github.com/mailru/easyjson v0.7.7 h1:UGYAvKxe3sBsEDzO8ZeWOSlIQfWFlxbzLZe7hwFURr0=
github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc=
github.com/openai/openai-go/v3 v3.38.0 h1:Kre0Fz9mPUxtWjRB/CoNBHflp9W7FkztOm/XMDr/A3E=
github.com/openai/openai-go/v3 v3.38.0/go.mod h1:cdufnVK14cWcT9qA1rRtrXx4FTRsgbDPW7Ia7SS5cZo=
github.com/pkoukk/tiktoken-go v0.1.8 h1:85ENo+3FpWgAACBaEUVp+lctuTcYUO7BtmfhlN/QTRo=
github.com/pkoukk/tiktoken-go v0.1.8/go.mod h1:9NiV+i9mJKGj1rYOT+njbv+ZwA/zJxYdewGl6qVatpg=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/rogpeppe/go-internal v1.14.1 h1:UQB4HGPB6osV0SQTLymcB4TgvyWu6ZyliaW0tI/otEQ=
github.com/rogpeppe/go-internal v1.14.1/go.mod h1:MaRKkUm5W0goXpeCfT7UZI6fk/L7L7so1lCWt35ZSgc=
github.com/standard-webhooks/standard-webhooks/libraries v0.0.1 h1:uOfcYT+3QungH6tIGSVCR/Y3KJmgJiHcojJbMTPDZAI=
github.com/standard-webhooks/standard-webhooks/libraries v0.0.1/go.mod h1:L1MQhA6x4dn9r007T033lsaZMv9EmBAdXyU/+EF40fo=
github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U=
github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U=
github.com/tidwall/gjson v1.14.2/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk=
github.com/tidwall/gjson v1.18.0 h1:FIDeeyB800efLX89e5a8Y0BNH+LOngJyGrIWxG2FKQY=
github.com/tidwall/gjson v1.18.0/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk=
github.com/tidwall/match v1.1.1 h1:+Ho715JplO36QYgwN9PGYNhgZvoUSc9X2c80KVTi+GA=
github.com/tidwall/match v1.1.1/go.mod h1:eRSPERbgtNPcGhD8UCthc6PmLEQXEWd3PRB5JTxsfmM=
github.com/tidwall/pretty v1.2.0/go.mod h1:ITEVvHYasfjBbM0u2Pg8T2nJnzm8xPwvNhhsoaGGjNU=
github.com/tidwall/pretty v1.2.1 h1:qjsOFOWWQl+N3RsoF5/ssm1pHmJJwhjlSbZ51I6wMl4=
github.com/tidwall/pretty v1.2.1/go.mod h1:ITEVvHYasfjBbM0u2Pg8T2nJnzm8xPwvNhhsoaGGjNU=
github.com/tidwall/sjson v1.2.5 h1:kLy8mja+1c9jlljvWTlSazM7cKDRfJuR/bOJhcY5NcY=
github.com/tidwall/sjson v1.2.5/go.mod h1:Fvgq9kS/6ociJEDnK0Fk1cpYF4FIW6ZF7LAe+6jwd28=
github.com/wk8/go-ordered-map/v2 v2.1.8 h1:5h/BUHu93oj4gIdvHHHGsScSTMijfx5PeYkE/fJgbpc=
github.com/wk8/go-ordered-map/v2 v2.1.8/go.mod h1:5nJHM5DyteebpVlHnWMV0rPz6Zp7+xBAnxjb1X5vnTw=
go.opentelemetry.io/auto/sdk v1.2.1 h1:jXsnJ4Lmnqd11kwkBV2LgLoFMZKizbCi5fNZ/ipaZ64=
go.opentelemetry.io/auto/sdk v1.2.1/go.mod h1:KRTj+aOaElaLi+wW1kO/DZRXwkF4C5xPbEe3ZiIhN7Y=
go.opentelemetry.io/otel v1.43.0 h1:mYIM03dnh5zfN7HautFE4ieIig9amkNANT+xcVxAj9I=
Expand Down Expand Up @@ -55,6 +90,8 @@ go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto=
go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE=
golang.org/x/net v0.52.0 h1:He/TN1l0e4mmR3QqHMT2Xab3Aj3L9qjbhRm78/6jrW0=
golang.org/x/net v0.52.0/go.mod h1:R1MAz7uMZxVMualyPXb+VaqGSa3LIaUqk0eEt3w36Sw=
golang.org/x/sync v0.20.0 h1:e0PTpb7pjO8GAtTs2dQ6jYa5BWYlMuX047Dco/pItO4=
golang.org/x/sync v0.20.0/go.mod h1:9xrNwdLfx4jkKbNva9FpL6vEN7evnE43NNNJQ2LF3+0=
golang.org/x/sys v0.42.0 h1:omrd2nAlyT5ESRdCLYdm3+fMfNFE/+Rf4bDIQImRJeo=
golang.org/x/sys v0.42.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw=
golang.org/x/text v0.35.0 h1:JOVx6vVDFokkpaq1AEptVzLTpDe9KGpj5tR4/X+ybL8=
Expand All @@ -69,5 +106,10 @@ google.golang.org/grpc v1.80.0 h1:Xr6m2WmWZLETvUNvIUmeD5OAagMw3FiKmMlTdViWsHM=
google.golang.org/grpc v1.80.0/go.mod h1:ho/dLnxwi3EDJA4Zghp7k2Ec1+c2jqup0bFkw07bwF4=
google.golang.org/protobuf v1.36.11 h1:fV6ZwhNocDyBLK0dj+fg8ektcVegBBuEolpbTQyBNVE=
google.golang.org/protobuf v1.36.11/go.mod h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j23XfzDpco=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
gopkg.in/yaml.v2 v2.2.8 h1:obN1ZagJSUGI0Ek/LBmuj4SNLPfIny3KsKFopxRdj10=
gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
18 changes: 11 additions & 7 deletions internal/agent/agent.go
Original file line number Diff line number Diff line change
Expand Up @@ -661,9 +661,11 @@ func (a *Agent) executePlanPhase(ctx context.Context, newPath, rawDiff, changeFi
}
rec.SetResponse(resp, time.Since(startTime))
if resp.Usage != nil {
atomic.AddInt64(&a.totalTokensUsed, int64(resp.Usage.TotalTokens))
atomic.AddInt64(&a.totalInputTokens, int64(resp.Usage.PromptTokens+resp.Usage.CacheReadTokens))
atomic.AddInt64(&a.totalOutputTokens, int64(resp.Usage.CompletionTokens+resp.Usage.CacheWriteTokens))
input := int64(resp.Usage.PromptTokens + resp.Usage.CacheReadTokens)
output := int64(resp.Usage.CompletionTokens + resp.Usage.CacheWriteTokens)
atomic.AddInt64(&a.totalTokensUsed, input+output)
atomic.AddInt64(&a.totalInputTokens, input)
atomic.AddInt64(&a.totalOutputTokens, output)
}
fmt.Fprintf(stdout.Writer(), "[ocr] Plan completed for %s\n", newPath)
return resp.Content(), nil
Expand Down Expand Up @@ -740,11 +742,13 @@ func (a *Agent) performLlmCodeReview(ctx context.Context, messages []llm.Message
}
rec.SetResponse(resp, duration)
// Record LLM metrics with token info from API response usage field.
totalTokens := int64(0)
var totalTokens int64
if resp.Usage != nil {
totalTokens = resp.Usage.TotalTokens
atomic.AddInt64(&a.totalInputTokens, int64(resp.Usage.PromptTokens+resp.Usage.CacheReadTokens))
atomic.AddInt64(&a.totalOutputTokens, int64(resp.Usage.CompletionTokens+resp.Usage.CacheWriteTokens))
input := int64(resp.Usage.PromptTokens + resp.Usage.CacheReadTokens)
output := int64(resp.Usage.CompletionTokens + resp.Usage.CacheWriteTokens)
totalTokens = input + output
atomic.AddInt64(&a.totalInputTokens, input)
atomic.AddInt64(&a.totalOutputTokens, output)
}
telemetry.RecordLLMRequest(ctx, a.args.Model, duration, totalTokens, "ok")
atomic.AddInt64(&a.totalTokensUsed, totalTokens)
Expand Down
Loading