Help us shape the future of Go! Take the 2026 Go Developer Survey

aikit

package
v0.17.0 Latest Latest
Warning

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

Go to latest
Published: Oct 29, 2026 License: AGPL-3.0 Imports: 13 Imported by: 0

Documentation

Overview

Package aikit is what every AI adapter does the same way, whatever its provider: the prompts, the tools a model is given and their schemas, the budgeted reader a model reads the repository through, and what a tool call does. An adapter translates these into its provider's own request types and runs the conversation; nothing here speaks to a provider.

One copy, so the Anthropic, OpenAI and local adapters ask the same questions under the same rules (R-332 – R-334) and are refused the same way by core when a model breaks one. A second copy of a prompt is a copy that drifts.

Like every package under internal/adapter, this imports adapter/api and nothing of core's authorization, audit or state (R-027).

Index

Constants

View Source
const (
	ToolFindPeople = "find_people"
	ToolFindApps   = "find_apps"
	ToolFindGroups = "find_groups"

	// MaxLookups is the most lookups one conversation may make. Past it a
	// lookup is answered with a request to submit, not run, so a model that
	// keeps searching ends the conversation rather than growing it.
	MaxLookups = 10
)

The lookup tools (O-54): how a model drafting access or searching the audit log finds the people, apps and groups a request names, instead of being handed every one. Each runs a callback core passed in the request, as the person who asked; nothing here reads state (R-027).

View Source
const (
	ToolListFiles      = "list_files"
	ToolReadFile       = "read_file"
	ToolSubmitFindings = "submit_findings"

	// ToolSubmit is the one tool the administrative functions get.
	ToolSubmit = "submit"
)

The tools a repair, an answering or a revision call gets: two to read the repository, one to answer.

Answering through a tool rather than in prose is deliberate. The schema is the closed set from design 10 §3, so a malformed amendment is rejected before it reaches Pando, and the shape core validates is the shape the model was given. Parsing amendments out of prose would mean two descriptions of the same structure, kept in step by hand.

View Source
const AnswerThroughTool = " Answer only by calling the submit tool, once. Do not answer in prose."

AnswerThroughTool is added to every task's system prompt. The call is asked for rather than forced: current models refuse forced tool use.

View Source
const LookupNote = "The lists below are not every account, app or group: look up anyone or anything the " +
	"request names with the find tools, and use the IDs they return. Never make up an ID. If a lookup " +
	"finds nobody, leave them out and say so."

LookupNote is what a prompt says about the lookup tools, when it has them.

View Source
const MatchNote = "The lists below are not every account, app or group: they are the ones whose names " +
	"match words in the request. If someone or something the request names is not listed, leave it out " +
	"and say so; never make up an ID."

MatchNote is what a prompt says about lists core pre-searched, for an adapter without the lookup tools.

View Source
const MaxFileBytes = 64 << 10

MaxFileBytes is the most of one file a model is shown. Longer files are truncated, and say so.

View Source
const Voice = "Write for the person who asked, in plain sentences. Be specific and self-contained: " +
	"someone reading only your answer should understand it. No apology, no exclamation mark, " +
	"no \"Error:\" prefix, and do not restate the question."

Voice is the house style every answer here is held to (R-105), said once.

Variables

View Source
var ErrBudget = errors.New("budget spent")

Functions

func Call

func Call(name, input string, src *Reader) (string, bool)

Call runs one read tool and says what to hand back: the text, and whether it is an error.

A budget refusal comes back as an ordinary result rather than an error, so the model can submit what it has instead of the whole screening being lost over one file too many. That is the difference between a bounded screening and a failed one.

func Findings

func Findings(input string, src *Reader, model string) (api.ScreenResult, error)

Findings reads a submit_findings call's input.

Parsed rather than matched on the raw string: escaping in a tool input is the model's to choose, and string matching on it is how that bites.

func JSONBlock

func JSONBlock(v any) string

JSONBlock renders v for a prompt.

func KeyStep added in v0.10.0

func KeyStep(link api.Link, do ...api.Action) api.Step

KeyStep creates an API key at the provider and asks for it.

func Limit

func Limit[T int | int64](asked, own T) T

Limit takes the lower of what core asked for and what the adapter will do. Neither side raises the other's (design 10 §2).

func ModelStep added in v0.10.0

func ModelStep(links []api.Link, do ...api.Action) api.Step

ModelStep asks for the default model.

func Preloaded

func Preloaded(src *Reader, known []string) string

Preloaded reads the files an earlier call on this proposal read, and hands them over at the start: the model begins knowing what it knew last time, rather than listing and reading its way back there one round trip at a time. Read through the budgeted reader, so they count as reads and are recorded as sent (R-337); one the budget refuses, or that is gone, is left out.

func SystemPrompt

func SystemPrompt(fn api.AIFunction) string

SystemPrompt returns the job for fn, and the rules core will enforce anyway.

Stating the rules twice is not redundancy. Core refuses an amendment that breaks one of these (design 10 §3), so a model that has not been told is a model whose work is thrown away — the refusal is correct and the call is wasted. Telling it here is what turns a refusal into a rarity.

func UserPrompt

func UserPrompt(fn api.AIFunction, req api.ScreenRequest) string

UserPrompt is the proposal, rendered.

Types

type Looker

type Looker struct {
	// contains filtered or unexported fields
}

Looker runs the lookup tools for one conversation, counting them.

func NewLooker

func NewLooker(l *api.Lookup) *Looker

NewLooker returns a Looker over l, which may be nil.

func (*Looker) Call

func (k *Looker) Call(ctx context.Context, name, input string) (string, bool)

Call runs one lookup and says what to hand back: the text, and whether it is an error. The result is at most LookupLimit matches, each field clipped.

func (*Looker) Calls

func (k *Looker) Calls() int

Calls is how many lookups have run.

func (*Looker) Handles

func (k *Looker) Handles(name string) bool

Handles reports whether name is a lookup tool this conversation offers.

type Reader

type Reader struct {
	// contains filtered or unexported fields
}

Reader is the repository, bounded.

Every read goes through here rather than straight to the SourceView, for two reasons that are not the same. The budget (R-339) is one: a screening has a ceiling in files and bytes and something has to hold the running total. The other is that this is the list of what left the host, which R-337 says the audit event carries — and a count kept beside the reads is a count that cannot disagree with them.

func NewReader

func NewReader(src api.SourceView, maxFiles int, maxBytes int64) *Reader

func (*Reader) Files

func (r *Reader) Files() []string

Files is what was read, in the order it was read, for the audit event.

func (*Reader) Glob

func (r *Reader) Glob(pattern string) ([]string, error)

Glob lists paths without counting against the file budget.

Listing is not reading. The budget exists to bound what content leaves the host, and a list of names is how the model decides which of them to spend the budget on — charging for it would make an efficient screening impossible.

func (*Reader) Open

func (r *Reader) Open(name string) (string, error)

Open reads one file, or says why it did not.

type Task

type Task struct {
	System string
	User   string
	Tool   Tool

	// Lookup, when set, is what the lookup tools run (O-54).
	Lookup *api.Lookup
}

Task is one administrative request: what the model is told, what it is asked, and the tool it answers through.

func AccessTask

func AccessTask(req api.AccessRequest) Task

AccessTask drafts a role and, optionally, a group (R-343).

func AuditSearchTask

func AuditSearchTask(req api.AuditSearchRequest) Task

AuditSearchTask turns a question into one audit filter (R-345).

func AuditSummaryTask

func AuditSummaryTask(req api.AuditSummaryRequest) Task

AuditSummaryTask summarizes the records core found (R-345).

func PolicyTask

func PolicyTask(req api.PolicyRequest) Task

PolicyTask proposes changes to host policy (R-344).

func ReferenceTask

func ReferenceTask(req api.ReferenceRequest) Task

ReferenceTask answers from the generated reference (R-346).

func (Task) Tools

func (t Task) Tools() []Tool

Tools are the task's tools: the one it answers through, then the lookups.

type Tool

type Tool struct {
	Name        string
	Description string

	// Properties and Required are the input schema's; the schema is always an
	// object.
	Properties map[string]any
	Required   []string

	// Strict asks a provider that can to validate the input against the
	// schema before the call reaches Pando.
	Strict bool
}

Tool is one tool, as every provider's tool definition needs it: a name, a description, and a JSON Schema object for its input.

func LookupTools

func LookupTools(l *api.Lookup) []Tool

LookupTools are the tools l offers: one per lookup the person may make.

func ScreenTools

func ScreenTools(fn api.AIFunction, questions []api.Question, values []string) []Tool

ScreenTools are the tools for fn: list, read, and submit findings.

func (Tool) Schema

func (t Tool) Schema() map[string]any

Schema is the tool's input schema as a whole JSON Schema object.

Directories

Path Synopsis
Package aitest holds what the AI adapters' tests share: a repository in memory.
Package aitest holds what the AI adapters' tests share: a repository in memory.

Jump to

Keyboard shortcuts

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