Skip to content

Canonical BCP-47 casing in verbose logs; always request OpenAI stream usage#51

Merged
min0625 merged 1 commit into
mainfrom
feat/canonical-lang-tag-and-openai-stream-options
Jul 1, 2026
Merged

Canonical BCP-47 casing in verbose logs; always request OpenAI stream usage#51
min0625 merged 1 commit into
mainfrom
feat/canonical-lang-tag-and-openai-stream-options

Conversation

@min0625

@min0625 min0625 commented Jul 1, 2026

Copy link
Copy Markdown
Owner

Summary

  • Verbose diagnostics now display language tags in conventional BCP-47 casing (zh-twzh-TW, zh-hantzh-Hant) via a new canonicalLangTag helper, instead of the lowercase-normalized internal form used for matching.
  • OpenAI client always sets stream_options.include_usage, since every documented compatible endpoint (OpenAI, Ollama, LM Studio) either supports it or ignores unknown fields — removes the defaultEndpoint special-casing that previously omitted it for custom base URLs.
  • Excludes _test.go files from the goconst linter and drops the resulting test-only string constants in main_test.go.
  • Updates docs/manual-testing.md with the new casing behavior, an MINT_BASE_URL-without-API-key example, MINT_VERBOSE env var section, and a Ctrl+C/SIGTERM timing note.

Test plan

  • make check passes (pre-commit hook re-ran it)
  • New/updated unit tests: TestCanonicalLangTag, TestCompleteRequestsStreamOptions (renamed from the old omit-for-custom-base-URL test), TestCompleteSurfaces400

…stream usage

Verbose diagnostics now render language tags in conventional casing
(zh-tw -> zh-TW, zh-hant -> zh-Hant) via a new canonicalLangTag helper,
instead of the lowercase-normalized internal form.

The OpenAI client now always sets stream_options.include_usage, since
every documented compatible endpoint (OpenAI, Ollama, LM Studio) either
supports it or ignores unknown fields; this drops the defaultEndpoint
special-casing and its dead branch for custom base URLs.

Also excludes _test.go files from the goconst linter and drops the
resulting test-only string constants in main_test.go for readability.
@codecov

codecov Bot commented Jul 1, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 92.00000% with 2 lines in your changes missing coverage. Please review.

Files with missing lines Patch % Lines
cmd/mint/main.go 88.88% 2 Missing ⚠️

📢 Thoughts on this report? Let us know!

@qodo-code-review

Copy link
Copy Markdown

PR Summary by Qodo

Canonical BCP-47 casing in verbose logs; always request OpenAI stream usage

✨ Enhancement 🧪 Tests 📝 Documentation 🕐 10-20 Minutes

Grey Divider

AI Description

• Adds canonicalLangTag helper to format normalized BCP-47 tags with conventional casing (zh-twzh-TW, zh-hantzh-Hant) for verbose diagnostic output only; internal matching still uses
 lowercase.
• Removes the defaultEndpoint flag from the OpenAI client — stream_options.include_usage is now
 always sent, since Ollama and LM Studio either support it or ignore unknown fields.
• Excludes _test.go files from the goconst linter and removes the now-unnecessary test-only
 string constants (langZhTW, argTarget, etc.) from main_test.go.
• Expands docs/manual-testing.md with updated BCP-47 casing examples, a
 MINT_BASE_URL-without-API-key scenario, a new MINT_VERBOSE env var section, and a Ctrl+C/SIGTERM
 timing clarification.
Diagram

graph TD
    A["cmd/mint/main.go"] -->|"calls for display"| B["canonicalLangTag()"]
    A -->|"logv() calls"| C["Verbose stderr output"]
    B --> C
    D["openai.Client"] -->|"always sends"| E["stream_options.include_usage"]
    F[".golangci.yaml"] -->|"excludes goconst"| G["_test.go files"]
    H["main_test.go"] -->|"removes constants, adds"| I["TestCanonicalLangTag"]
    D -->|"renamed test"| J["TestCompleteRequestsStreamOptions"]
    D -->|"new test"| K["TestCompleteSurfaces400"]

    subgraph Legend
      direction LR
      _file["Source File"] ~~~ _helper(["Helper / Output"]) ~~~ _cfg{{"Config"}}
    end
Loading
High-Level Assessment

The following are alternative approaches to this PR:

1. Use golang.org/x/text/language for BCP-47 canonicalization
  • ➕ Handles all BCP-47 subtag types correctly (3-letter regions, variant subtags, grandfathered tags)
  • ➕ No custom parsing logic to maintain
  • ➖ Adds a non-trivial dependency for a display-only helper
  • ➖ Overkill for the small, controlled set of tags the tool actually uses

Recommendation: The PR's approach is appropriate for both changes. For canonicalLangTag, using golang.org/x/text/language (the standard BCP-47 parser) would be more correct for edge cases (3-letter region codes, variant subtags, grandfathered tags), but the project's actual tag set is small and well-defined, making a lightweight hand-rolled helper a reasonable trade-off that avoids a new dependency. For stream_options, always sending it is the right call given the documented endpoint set; the previous conditional was defensive code that added complexity without a known failing case.

Files changed (7) +179 / -69

Enhancement (2) +52 / -26
main.goAdd canonicalLangTag helper; use it in all verbose log calls +33/-3

Add canonicalLangTag helper; use it in all verbose log calls

• Introduces 'canonicalLangTag', a pure presentation helper that reformats a normalized (lowercase) BCP-47 tag into conventional casing: lowercase primary subtag, uppercase 2-letter region (e.g. 'zh-TW'), title-case 4-letter script (e.g. 'zh-Hant'). All three 'logv' calls that emit language tags now pass through this helper, so verbose output shows human-readable casing without affecting internal matching.

cmd/mint/main.go

openai.goAlways send stream_options.include_usage; remove defaultEndpoint field +19/-23

Always send stream_options.include_usage; remove defaultEndpoint field

• Removes the 'defaultEndpoint bool' field and the conditional that previously omitted 'stream_options.include_usage' for custom base URLs. The field is now unconditionally set in every request body, with a doc comment explaining that all documented compatible endpoints (OpenAI, Ollama, LM Studio) either support it or ignore unknown fields.

internal/provider/openai/openai.go

Tests (2) +86 / -40
main_test.goAdd TestCanonicalLangTag; remove test-only string constants +52/-35

Add TestCanonicalLangTag; remove test-only string constants

• Adds 'TestCanonicalLangTag' with 9 cases covering empty input, language-only tags, region uppercasing, script title-casing, idempotency, and mixed-case normalization. Removes the 'langZhTW', 'langZhTw', 'argTarget', 'inputHello', and 'inputhello' constants (now redundant after the 'goconst' linter exclusion) and inlines their literal values at each call site.

cmd/mint/main_test.go

openai_test.goRename stream_options test; add TestCompleteSurfaces400 +34/-5

Rename stream_options test; add TestCompleteSurfaces400

• Renames 'TestCompleteOmitsStreamOptionsForCustomBaseURL' to 'TestCompleteRequestsStreamOptions' and inverts its assertion to verify 'stream_options' is always present. Adds 'TestCompleteSurfaces400' which confirms a 400 response is surfaced verbatim as an error in exactly one request with no retry.

internal/provider/openai/openai_test.go

Documentation (2) +35 / -3
manual-testing.mdUpdate verbose output examples; add MINT_VERBOSE and MINT_BASE_URL sections +33/-3

Update verbose output examples; add MINT_VERBOSE and MINT_BASE_URL sections

• Updates all verbose log examples to show canonical BCP-47 casing ('zh-TW', 'zh-HK' instead of lowercase). Adds a 'MINT_BASE_URL'-without-API-key example to the error cases section, a new section 14 documenting the 'MINT_VERBOSE' env var with sample output, and a clarification on Ctrl+C/SIGTERM timing.

docs/manual-testing.md

core.mdAdd writer.go and httpx.go to the source map +2/-0

Add writer.go and httpx.go to the source map

• Adds two previously undocumented internal packages — 'internal/llm/writer.go' (TrailingNewlineWriter) and 'internal/httpx/httpx.go' (shared HTTP client) — to the Serena agent memory source map.

.serena/memories/core.md

Other (1) +6 / -0
.golangci.yamlExclude _test.go files from goconst linter +6/-0

Exclude _test.go files from goconst linter

• Adds an 'exclusions.rules' entry that disables the 'goconst' linter for all '_test.go' files, allowing test code to use inline string literals without triggering constant-extraction warnings.

.golangci.yaml

@qodo-code-review

Copy link
Copy Markdown

Code Review by Qodo

🐞 Bugs (1) 📘 Rule violations (0) 📜 Skill insights (0)

Context used
✅ Compliance rules (platform): 36 rules

Grey Divider


Remediation recommended

1. Strict servers reject stream_options 🐞 Bug ☼ Reliability
Description
openai.Client.Complete now always sends stream_options.include_usage, so OpenAI-compatible
servers behind a custom MINT_BASE_URL that reject unknown fields will fail the request. The client
surfaces the 4xx error and does not retry without stream_options, so translation breaks for those
deployments.
Code

internal/provider/openai/openai.go[R101-110]

	body := requestBody{
		Model: c.modelName,
		Messages: []message{
			{Role: "system", Content: system},
			{Role: "user", Content: user},
		},
-		Temperature: temperature,
-		Stream:      true,
-	}
-
-	// stream_options.include_usage is an OpenAI extension. Only request it on
-	// the official endpoint; local/proxy servers reached via a custom base URL
-	// may reject unknown fields, so omit it there.
-	if c.defaultEndpoint {
-		body.StreamOptions = &streamOptions{IncludeUsage: true}
+		Temperature:   temperature,
+		Stream:        true,
+		StreamOptions: &streamOptions{IncludeUsage: true},
	}
Evidence
The PR makes StreamOptions non-nil unconditionally, which forces stream_options into the JSON
payload (omitempty only omits nil). The repo supports using a custom BaseURL (proxy/local
server), and the new tests explicitly assert stream_options is always present and that 400 errors
are surfaced with no recovery.

internal/provider/openai/openai.go[56-110]
internal/provider/config.go[28-52]
internal/provider/openai/openai_test.go[141-196]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

### Issue description
The OpenAI client now always includes `stream_options.include_usage` in requests. Some OpenAI-compatible servers (often used via `MINT_BASE_URL`) can reject unknown fields; when that happens the request fails and the client does not attempt a fallback retry.

### Issue Context
- `requestBody.StreamOptions` is a pointer with `omitempty`; setting it non-nil always sends the JSON field.
- The project explicitly supports `MINT_BASE_URL` mode (proxy/local server).
- Tests state 400s are surfaced “as-is” with no recovery.

### Fix Focus Areas
- internal/provider/openai/openai.go[91-135]
- internal/provider/openai/openai_test.go[141-196]

### Suggested approach
- On non-200 responses (especially 400), detect the specific “unknown field”/schema error pattern (if present) and retry exactly once with `StreamOptions=nil`.
- Keep the original error if the retry also fails (or if the error is not a schema/unknown-field type).
- Add/adjust a unit test: server returns 400 when `stream_options` present, returns 200/stream when absent; assert two requests max and successful completion.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


Grey Divider

Qodo Logo

Comment on lines 101 to 110
body := requestBody{
Model: c.modelName,
Messages: []message{
{Role: "system", Content: system},
{Role: "user", Content: user},
},
Temperature: temperature,
Stream: true,
}

// stream_options.include_usage is an OpenAI extension. Only request it on
// the official endpoint; local/proxy servers reached via a custom base URL
// may reject unknown fields, so omit it there.
if c.defaultEndpoint {
body.StreamOptions = &streamOptions{IncludeUsage: true}
Temperature: temperature,
Stream: true,
StreamOptions: &streamOptions{IncludeUsage: true},
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Remediation recommended

1. Strict servers reject stream_options 🐞 Bug ☼ Reliability

openai.Client.Complete now always sends stream_options.include_usage, so OpenAI-compatible
servers behind a custom MINT_BASE_URL that reject unknown fields will fail the request. The client
surfaces the 4xx error and does not retry without stream_options, so translation breaks for those
deployments.
Agent Prompt
### Issue description
The OpenAI client now always includes `stream_options.include_usage` in requests. Some OpenAI-compatible servers (often used via `MINT_BASE_URL`) can reject unknown fields; when that happens the request fails and the client does not attempt a fallback retry.

### Issue Context
- `requestBody.StreamOptions` is a pointer with `omitempty`; setting it non-nil always sends the JSON field.
- The project explicitly supports `MINT_BASE_URL` mode (proxy/local server).
- Tests state 400s are surfaced “as-is” with no recovery.

### Fix Focus Areas
- internal/provider/openai/openai.go[91-135]
- internal/provider/openai/openai_test.go[141-196]

### Suggested approach
- On non-200 responses (especially 400), detect the specific “unknown field”/schema error pattern (if present) and retry exactly once with `StreamOptions=nil`.
- Keep the original error if the retry also fails (or if the error is not a schema/unknown-field type).
- Add/adjust a unit test: server returns 400 when `stream_options` present, returns 200/stream when absent; assert two requests max and successful completion.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools

@min0625 min0625 merged commit d8ca588 into main Jul 1, 2026
6 checks passed
@min0625 min0625 deleted the feat/canonical-lang-tag-and-openai-stream-options branch July 1, 2026 07:08
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant