Temperature and TopP are *float64 rather than float64. That is not
stylistic — it is the only way to distinguish "use the provider's default" from
"use exactly zero", and the difference is load-bearing.
You will learn
- Why the sampling fields are pointers
- Why skyl always sends a non-nil value, even to models that reject it
- How
Stopsequences behave across providers - What
Thinkingdoes, and why it is the least uniform field in the library
The pointer#
Temperature *float64
TopP *float64Temperature *float64
TopP *float64nil means "do not send this field at all" — the provider applies its own
default. A non-nil pointer means "send exactly this".
With a plain float64, 0 would be indistinguishable from unset, and
temperature zero is a value people genuinely want.
func f(v float64) *float64 { return &v }
req := &skyl.Request{
Model: "gpt-5.6",
MaxTokens: 512,
Temperature: f(0), // explicitly deterministic — NOT "unset"
Messages: []skyl.Message{skyl.UserText("Extract the invoice number.")},
}func f(v float64) *float64 { return &v }
req := &skyl.Request{
Model: "gpt-5.6",
MaxTokens: 512,
Temperature: f(0), // explicitly deterministic — NOT "unset"
Messages: []skyl.Message{skyl.UserText("Extract the invoice number.")},
}skyl always sends what you set#
This is one of the clearest expressions of skyl's "never silently drop data" rule: the library would rather hand you a 400 that names the problem than produce a request that quietly is not what you wrote.
Stop sequences#
req.Stop = []string{"\n\nUser:", "END"}req.Stop = []string{"\n\nUser:", "END"}Mapped on all four adapters — stop_sequences on Anthropic, stop on the
OpenAI-format adapters, generationConfig.stopSequences on Gemini.
MaxTokens#
req.MaxTokens = 1024req.MaxTokens = 1024Zero means the provider's default — except on Anthropic, whose API requires the field, so that adapter supplies 4096 rather than failing a request every other provider would accept. It is the only substituted default in the library.
A negative value is rejected locally by Validate.
Hitting the cap gives you StopReason == StopMaxTokens, and the response is
truncated. Treat it as incomplete rather than as a short answer.
Thinking#
type Thinking struct {
Enabled bool
Effort Effort // low, medium, high, max
}type Thinking struct {
Enabled bool
Effort Effort // low, medium, high, max
}A nil pointer and a zero value mean different things: nil is "provider
default", &Thinking{} is "explicitly off".
This is the least uniform field in skyl, and it is worth reading the row for your provider before relying on it:
| Value | anthropic | openai / compat | gemini |
|---|---|---|---|
| nil | nothing sent | nothing sent | nothing sent |
| {Enabled: true} | thinking: adaptive | ignored | budget -1 |
| {Enabled: false} | thinking: disabled | ignored | budget 0 |
| {Enabled: true, Effort: low} | effort ignored | reasoning_effort: low | budget 1024 |
| … Effort: max | effort ignored | sent — OpenAI does not define it, expect 400 | budget 24576 |
Two consequences worth stating plainly:
- On OpenAI,
&Thinking{Enabled: false}does nothing. If you are turning reasoning off to control cost, it will not work there. UseProviderOptionsto sendreasoning_effortdirectly. - On Anthropic,
Effortdoes nothing. The SDK's adaptive thinking config has no budget field, so there is nothing to map it onto. Reach the budget withProviderOptions: {"thinking.budget_tokens": 4096}.
Recap
TemperatureandTopPare pointers so that zero is distinguishable from unset.- A non-nil value is always sent, even to models that reject it — silence would be worse.
Stopis mapped everywhere, but detecting that it fired is Anthropic-only.MaxTokenszero means the provider default, except Anthropic, where skyl supplies 4096.Thinkingis the least portable field in the library. Check the matrix row for your provider.
Try out some challenges
Each of these is solvable with what this page covered. Run them against the sandbox — no API key needed.
Turn reasoning off on all three providers
&Thinking{Enabled: false} does nothing on OpenAI. Write a helper that
genuinely disables reasoning wherever it can.
Show hint
Anthropic and Gemini honour the field. OpenAI needs ProviderOptions.
Show solution
func noThinking(req *skyl.Request, provider string) {
req.Thinking = &skyl.Thinking{Enabled: false}
if provider == "openai" || provider == "openai-compatible" {
// The adapter ignores Thinking unless Effort is set, so reach the wire
// field directly. This is a shallow top-level merge, which is fine
// here because reasoning_effort is a top-level key.
if req.ProviderOptions == nil {
req.ProviderOptions = map[string]any{}
}
req.ProviderOptions["reasoning_effort"] = "minimal"
}
}func noThinking(req *skyl.Request, provider string) {
req.Thinking = &skyl.Thinking{Enabled: false}
if provider == "openai" || provider == "openai-compatible" {
// The adapter ignores Thinking unless Effort is set, so reach the wire
// field directly. This is a shallow top-level merge, which is fine
// here because reasoning_effort is a top-level key.
if req.ProviderOptions == nil {
req.ProviderOptions = map[string]any{}
}
req.ProviderOptions["reasoning_effort"] = "minimal"
}
}Note the shallow-merge caveat does not bite here because reasoning_effort is
top-level. It would bite if you were setting something inside
generationConfig.
Detect a truncated answer
StopMaxTokens means the response is incomplete. Handle it rather than
presenting a half-sentence as an answer.
Show hint
You can either raise MaxTokens and retry, or tell the caller it was cut off.
Do not silently pretend it is complete.
Show solution
resp, err := client.Complete(ctx, req)
if err != nil {
return err
}
if resp.StopReason == skyl.StopMaxTokens {
return fmt.Errorf("answer truncated at %d tokens; raise MaxTokens", req.MaxTokens)
}resp, err := client.Complete(ctx, req)
if err != nil {
return err
}
if resp.StopReason == skyl.StopMaxTokens {
return fmt.Errorf("answer truncated at %d tokens; raise MaxTokens", req.MaxTokens)
}Silently returning the truncated text is how a summariser ends up producing summaries that stop mid-word, and nobody notices for a week because the output still looks plausible.