Files
zeekay 999e2153b3 Type every wire: bytes, forms, streams, sockets, redirects, unions
A typed op could only take one JSON value and answer one, so uploads,
signed webhooks, form posts, YAML and merge-patch bodies, PDFs, SSE,
WebSockets, OAuth redirects and upstream relays stayed untyped routes
with no schema, tool, command or SDK method. Each is now a typed op and
its REST wire is unchanged.

Requests: a zip.Body field takes the body and Content-Type exactly as
sent; an In implementing Parser decodes itself; form: fields and
*zip.File parts bind url-encoded and multipart forms; cookie: binds a
request cookie as an in: cookie parameter. Consumes names the media.

Answers: zip.Body (bytes, filename, its own status and headers, a
Reader streams chunked), Verbatim[T] (a relay documented as T), Sse[T]
(events with keep-alive), Socket[M] (an upgrade after the contract
runs; 426 without one, 501 over ZAP), Redirect, CookieCoder, Produces,
and unions: a type whose OneOf method's results are the alternatives,
with Or[A,B] as the stock one filing each under the status it states.

Projections follow from the manifest: the document publishes binary
media, form schemas, x-events, x-socket with 101, 3xx with Location
and oneOf; MCP takes bytes as base64 and returns bytes, text or
collected events as tool content, listing a socket op in _meta as no
tool; the CLI reads --body @file|-, --field name=@file and --out,
prints events as they arrive and bridges a socket to stdin and stdout.
tools/list is ProjectMCP over the manifest, the projection MCPTools
already was.

Prose reaches promoted fields (filed under the declaring struct), a
type defined over another struct, and the type arguments of zip's
kinds and OneOf results; an instantiated generic is published under
its name followed by its argument names instead of an illegal
component key. The unused reflect-only document and tool builders go.
2026-10-09 09:42:00 -07:00

77 lines
1.9 KiB
Go

// Package kinds answers with zip's kinds: an event stream, a union, an
// either-or and a relay, each of whose wire shapes is a type argument or a
// OneOf result.
package kinds
import (
"context"
"github.com/zap-proto/zip"
)
// Chunk is one event of a completion.
type Chunk struct {
// Text is the next piece of the completion.
Text string `json:"text"`
}
// Done is a completion answered whole.
type Done struct {
// Answer is the whole completion.
Answer string `json:"answer"`
}
// Upstream is the upstream's answer.
type Upstream struct {
// Model is the model the upstream ran.
Model string `json:"model"`
}
// Event is one ingested event.
type Event struct {
// Kind names the event.
Kind string `json:"kind"`
}
// Batch is several events at once.
type Batch struct {
// Events are the events, oldest first.
Events []Event `json:"events"`
}
// Ingest is one event or a batch.
type Ingest struct{ one *Event }
// OneOf declares the alternatives.
func (Ingest) OneOf() (Event, Batch) { return Event{}, Batch{} }
// Prompt asks for a completion.
type Prompt struct {
// Stream asks for events rather than one answer.
Stream bool `json:"stream"`
}
// Stream streams a completion.
func Stream(ctx context.Context, in *Prompt) (*zip.Sse[Chunk], error) { return &zip.Sse[Chunk]{}, nil }
// Either answers whole or streams, as asked.
func Either(ctx context.Context, in *Prompt) (*zip.Or[Done, zip.Sse[Chunk]], error) {
return &zip.Or[Done, zip.Sse[Chunk]]{}, nil
}
// Relay passes the upstream's answer through as sent.
func Relay(ctx context.Context, in *Prompt) (*zip.Verbatim[Upstream], error) {
return &zip.Verbatim[Upstream]{}, nil
}
// Take ingests what was sent.
func Take(ctx context.Context, in *Ingest) (*struct{}, error) { return nil, nil }
// Register declares the ops.
func Register(app *zip.App) {
app.Post("/v1/stream", Stream)
app.Post("/v1/either", Either)
app.Post("/v1/relay", Relay)
app.Post("/v1/ingest", Take)
}