chatclient

package
v0.13.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Aug 31, 2026 License: Apache-2.0 Imports: 15 Imported by: 0

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

View Source
var (
	ErrNilModel             = errors.New("chatclient: nil model")
	ErrNilClient            = errors.New("chatclient: nil client")
	ErrStreamingUnsupported = errors.New("chatclient: streaming unsupported")
)
View Source
var (
	ErrInvalidOutputFormat = errors.New("chatclient: invalid output format")

	ErrInvalidOutput = errors.New("chatclient: invalid output")
)
View Source
var ErrInvalidTemplate = errors.New("chatclient: invalid template")
View Source
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 New

func New(model chat.Model, config Config) (Client, error)

func (Client) Call

func (c Client) Call(ctx context.Context, req *chat.Request) (*chat.Response, error)

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 JSONSchemaConfig struct {
	Name        string
	Description string
}

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.

func ParseTemplate

func ParseTemplate(source string) (*Template, error)

func (*Template) Render

func (t *Template) Render(data any) (string, error)

func (*Template) Require

func (t *Template) Require(names ...string) error

func (*Template) Source

func (t *Template) Source() string

func (*Template) SystemMessage

func (t *Template) SystemMessage(data any) (chat.Message, error)

func (*Template) UserMessage

func (t *Template) UserMessage(data any, attachments ...*media.Media) (chat.Message, error)

Directories

Path Synopsis
Package safeguard provides fail-closed input and output screening as Core chat middleware.
Package safeguard provides fail-closed input and output screening as Core chat middleware.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL