Documentation
¶
Overview ¶
Package chatclient provides direct, optional conveniences around the minimal chat protocols and model capabilities defined by Core. Client.Output applies a provider-neutral OutputFormat and strictly decodes the complete response. Typed output has only a terminal value; callers that need transport deltas use Client.Stream directly.
NewToolMiddleware covers the deliberately small direct-use path: it advertises a frozen executable Tool set, validates one returned call batch, executes it serially, and performs one follow-up model call. Further tool rounds, retries, concurrency, approval, and durable execution belong to Agent.
Example ¶
package main
import (
"context"
"fmt"
"github.com/Tangerg/scope/core/chat"
"github.com/Tangerg/scope/core/chatclient"
)
func main() {
model := chat.ModelFunc(func(context.Context, *chat.Request) (*chat.Response, error) {
return textResponse("Hello from the model"), nil
})
client, err := chatclient.New(model, chatclient.Config{})
if err != nil {
panic(err)
}
request, err := chat.NewRequest(chat.NewUserMessage(chat.NewTextPart("Hello")))
if err != nil {
panic(err)
}
request.Options.Model = "example"
response, err := client.Call(context.Background(), request)
if err != nil {
panic(err)
}
fmt.Println(response.Text())
}
func textResponse(text string) *chat.Response {
message := chat.NewAssistantMessage(chat.NewTextPart(text))
return &chat.Response{Output: &chat.Output{
Message: &message,
FinishReason: chat.FinishReasonStop,
}}
}
Output: Hello from the model
Index ¶
Examples ¶
Constants ¶
This section is empty.
Variables ¶
var ( ErrNilModel = errors.New("chatclient: nil model") ErrNilClient = errors.New("chatclient: nil client") ErrStreamingUnsupported = errors.New("chatclient: streaming unsupported") )
var ( ErrInvalidOutputFormat = errors.New("chatclient: invalid output format") ErrInvalidOutput = errors.New("chatclient: invalid output") )
var ErrInvalidTemplate = errors.New("chatclient: invalid template")
var (
ErrInvalidToolMiddleware = errors.New("chatclient: invalid tool middleware")
)
Functions ¶
func NewToolMiddleware ¶
func NewToolMiddleware(executables ...tool.Tool) (chat.CallMiddleware, error)
NewToolMiddleware keeps direct Client usage useful for one model-requested Tool batch. It advertises a frozen Tool set, validates and executes the first returned batch serially, then makes one follow-up model call. Further rounds and execution policy deliberately remain outside this boundary.
Types ¶
type Client ¶
type Client struct {
// contains filtered or unexported fields
}
Client is an immutable, concurrency-safe composition of chat capabilities and middleware. It does not make an underlying model concurrency safe; callers must still follow the model's concurrency contract.
Call and Stream accept ordinary chat.Request values directly. Client snapshots each request before invoking middleware or a provider, so those layers cannot mutate caller-owned protocol values. A configured Streamer takes precedence; otherwise New discovers the capability on the model. Stream remains lazy and reports unsupported streaming as its single terminal item.
func (Client) Output ¶
func (c Client) Output[T any](ctx context.Context, req *chat.Request, format OutputFormat[T]) (T, error)
Example ¶
package main
import (
"context"
"fmt"
"github.com/Tangerg/scope/core/chat"
"github.com/Tangerg/scope/core/chatclient"
)
func main() {
type answer struct {
Value int `json:"value"`
}
model := chat.ModelFunc(func(context.Context, *chat.Request) (*chat.Response, error) {
return textResponse(`{"value":42}`), nil
})
client, err := chatclient.New(model, chatclient.Config{})
if err != nil {
panic(err)
}
request, err := chat.NewRequest(chat.NewUserMessage(chat.NewTextPart("What is six times seven?")))
if err != nil {
panic(err)
}
result, err := client.Output(context.Background(), request, chatclient.JSON[answer]())
if err != nil {
panic(err)
}
fmt.Println(result.Value)
}
func textResponse(text string) *chat.Response {
message := chat.NewAssistantMessage(chat.NewTextPart(text))
return &chat.Response{Output: &chat.Output{
Message: &message,
FinishReason: chat.FinishReasonStop,
}}
}
Output: 42
func (Client) Stream ¶
func (c Client) Stream(ctx context.Context, req *chat.Request) iter.Seq2[*chat.ResponseDelta, error]
Example ¶
package main
import (
"context"
"fmt"
"iter"
"github.com/Tangerg/scope/core/chat"
"github.com/Tangerg/scope/core/chatclient"
)
func main() {
model := chat.ModelFunc(func(context.Context, *chat.Request) (*chat.Response, error) {
return textResponse("fallback"), nil
})
streamer := chat.StreamerFunc(func(context.Context, *chat.Request) iter.Seq2[*chat.ResponseDelta, error] {
return func(yield func(*chat.ResponseDelta, error) bool) {
if !yield(delta("Hello ", ""), nil) {
return
}
yield(delta("stream", chat.FinishReasonStop), nil)
}
})
client, err := chatclient.New(model, chatclient.Config{Streamer: streamer})
if err != nil {
panic(err)
}
request, err := chat.NewRequest(chat.NewUserMessage(chat.NewTextPart("Hello")))
if err != nil {
panic(err)
}
for response, streamErr := range client.Stream(context.Background(), request) {
if streamErr != nil {
panic(streamErr)
}
fmt.Print(response.Text())
}
fmt.Println()
}
func textResponse(text string) *chat.Response {
message := chat.NewAssistantMessage(chat.NewTextPart(text))
return &chat.Response{Output: &chat.Output{
Message: &message,
FinishReason: chat.FinishReasonStop,
}}
}
func delta(text string, reason chat.FinishReason) *chat.ResponseDelta {
return &chat.ResponseDelta{Parts: []chat.PartDelta{chat.NewTextDelta(text)}, FinishReason: reason}
}
Output: Hello stream
type Config ¶
type Config struct {
Streamer chat.Streamer
CallMiddleware []chat.CallMiddleware
StreamMiddleware []chat.StreamMiddleware
}
Config describes construction-time Client behavior. Request-specific values belong in chat.Request.
The slices are snapshotted by New, so callers may safely reuse or mutate their input after construction. The first middleware in each slice is the outermost wrapper, matching chat.Wrap and chat.WrapStream.
type JSONSchemaConfig ¶ added in v0.13.0
type OutputFormat ¶
type OutputFormat[T any] struct { // contains filtered or unexported fields }
OutputFormat couples a provider-neutral request contract with the decoder for its complete response. Pass one to Client.Output.
func JSON ¶
func JSON[T any]() OutputFormat[T]
func JSONSchema ¶
func JSONSchema[T any](config JSONSchemaConfig) (OutputFormat[T], error)
JSONSchema returns a result format coupled to the named contract derived from T.
func Text ¶
func Text() OutputFormat[string]
type Template ¶
type Template struct {
// contains filtered or unexported fields
}
Template is an immutable, parsed prompt template safe for concurrent use. Parsing fails on missing map keys instead of emitting placeholder text; Require inspects the complete parse tree, including pipelines and branches. Message projections validate the rendered protocol value and preserve media order without retaining per-render variables in Template.
Example ¶
package main
import (
"fmt"
"github.com/Tangerg/scope/core/chatclient"
)
func main() {
prompt, err := chatclient.ParseTemplate("Explain {{.Topic}} in one sentence.")
if err != nil {
panic(err)
}
message, err := prompt.UserMessage(struct{ Topic string }{Topic: "Go interfaces"})
if err != nil {
panic(err)
}
fmt.Println(message.Text())
}
Output: Explain Go interfaces in one sentence.