apiserver

package
v0.14.1 Latest Latest
Warning

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

Go to latest
Published: Apr 21, 2026 License: Apache-2.0 Imports: 34 Imported by: 0

Documentation

Overview

Package apiserver provides an HTTP server that auto-generates REST endpoints from AILANG module exports. It wraps the embed.Engine to expose AILANG functions as JSON-in/JSON-out HTTP endpoints.

Usage:

srv := apiserver.New(basePath, "8080")
srv.LoadModules([]string{"ecommerce/api/handlers.ail"})
srv.Start() // blocks

Index

Constants

View Source
const (
	// ErrCodeRouteNotFound is emitted when a request path does not match any
	// registered @route on a route-driven server (i.e. the server has at
	// least one @route registered). Carries did-you-mean suggestions.
	ErrCodeRouteNotFound = "ROUTE_NOT_FOUND"

	// ErrCodeModuleNotLoaded is emitted only on legacy (zero-@route) servers
	// when a /api/{module}/{func} dispatch targets a module that isn't
	// loaded. On route-driven servers, unmatched paths return
	// ErrCodeRouteNotFound instead.
	ErrCodeModuleNotLoaded = "MODULE_NOT_LOADED"

	// ErrCodeFunctionNotFound is emitted when the module is loaded but the
	// requested function does not exist OR is hidden via @noexpose. The
	// code is intentionally shared with hidden-export errors so @noexpose
	// remains indistinguishable from "genuinely missing" to external callers.
	ErrCodeFunctionNotFound = "FUNCTION_NOT_FOUND"

	// ErrCodeMethodNotAllowed is emitted when the request method doesn't
	// match the @route method (or isn't POST/GET for the catch-all handler).
	ErrCodeMethodNotAllowed = "METHOD_NOT_ALLOWED"
)

Router error codes. These are stable identifiers AI agents and SDKs can match on to decide how to recover from a router-layer failure.

These codes cover errors produced by the serve-api router and dispatch machinery BEFORE user code runs. Runtime errors from user code, coercion failures, and Result.Err → HTTP status mapping are covered by sibling sprints (M-DX-SERVE-API-ERROR-STATUS, M-DX-SERVE-API-COERCION).

View Source
const DefaultMaxUploadSize = 50 << 20

DefaultMaxUploadSize is the default maximum upload size (50MB).

Variables

This section is empty.

Functions

This section is empty.

Types

type Config

type Config struct {
	Port          string
	CORS          bool
	FrontendPath  string      // optional: React project path for Vite proxy
	StaticPath    string      // optional: built frontend files
	Watch         bool        // enable file watching for hot reload
	EffCtx        interface{} // optional: pre-configured effect context (*effects.EffContext)
	MCP           bool        // enable MCP endpoint at /mcp/
	MCPOnly       bool        // run as MCP stdio server only (no HTTP)
	A2A           bool        // enable A2A endpoints (/.well-known/agent.json, /a2a/)
	MaxUploadSize int64       // max upload size in bytes (0 = DefaultMaxUploadSize)
	APIKeyHeader  string      // HTTP header for API key auth (empty = no auth)
	APIKeyEnv     string      // env var containing expected API key
	LogLevel      int         // minimum severity for Debug output (0=DEBUG, 1=INFO, 2=WARN, 3=ERROR, 4=NONE)
	RoutesOnly    bool        // only expose @route-annotated functions as HTTP endpoints
}

Config holds configuration for the API server.

type ExportInfo

type ExportInfo struct {
	Name        string   `json:"name"`
	Type        string   `json:"type"`                   // human-readable type signature
	Pure        bool     `json:"pure"`                   // whether the function is pure
	Arity       int      `json:"arity"`                  // number of parameters (-1 if not a function)
	ParamNames  []string `json:"param_names,omitempty"`  // parameter names in order (for named JSON binding)
	ParamTypes  []string `json:"param_types,omitempty"`  // parameter type strings in order (for zero-value padding)
	RouteMethod string   `json:"route_method,omitempty"` // custom HTTP method from @route annotation
	RoutePath   string   `json:"route_path,omitempty"`   // custom URL path from @route annotation
	IsRaw       bool     `json:"is_raw,omitempty"`       // @raw annotation: pass full HttpRequest record
	IsNowrap    bool     `json:"is_nowrap,omitempty"`    // @nowrap annotation: skip FunctionCallResponse envelope
	IsNoExpose  bool     `json:"is_no_expose,omitempty"` // @noexpose annotation: hide from HTTP endpoints
	MCPName     string   `json:"mcp_name,omitempty"`     // @mcp_name annotation: explicit MCP tool name override
	DocComment  string   `json:"doc_comment,omitempty"`  // doc comment (-- lines) preceding the function
}

ExportInfo describes a single exported function from an AILANG module.

type FunctionCallRequest

type FunctionCallRequest struct {
	Args []interface{} `json:"args,omitempty"`
}

FunctionCallRequest is the JSON body for calling an AILANG function. Use "args" for positional arguments (array), or provide a flat JSON object which is passed as a single record argument.

type FunctionCallResponse

type FunctionCallResponse struct {
	Result      interface{}        `json:"result,omitempty"`
	Error       string             `json:"error,omitempty"`
	ErrorDetail *RouterErrorDetail `json:"error_detail,omitempty"`
	Module      string             `json:"module"`
	Func        string             `json:"func"`
	ElapsedMs   int64              `json:"elapsed_ms"`
}

FunctionCallResponse is the JSON response from a function call.

For error responses, the flat `Error` field is preserved for backward compatibility, and new clients should prefer the structured `ErrorDetail` envelope which carries a stable error code and optional did-you-mean hints. The flat `Error` string always mirrors `ErrorDetail.Message` when both are set.

type HealthResponse

type HealthResponse struct {
	Status       string `json:"status"`
	ModulesCount int    `json:"modules_count"`
	ExportsCount int    `json:"exports_count"`
}

HealthResponse is the response for GET /api/_health.

type MCPServer

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

MCPServer wraps an apiserver.Server to expose its functions as MCP tools.

func NewMCPServer

func NewMCPServer(srv *Server) *MCPServer

NewMCPServer creates an MCP server from an apiserver.Server. All loaded modules' exported functions are registered as MCP tools.

func (*MCPServer) HTTPHandler

func (ms *MCPServer) HTTPHandler() http.Handler

HTTPHandler returns an HTTP handler for the MCP server using streamable HTTP transport.

func (*MCPServer) RunStdio

func (ms *MCPServer) RunStdio(ctx context.Context) error

RunStdio runs the MCP server on stdio transport (blocking).

type ModuleInfo

type ModuleInfo struct {
	// Path is the RelPath projection (forward-slash relative path with
	// .ail stripped). Kept as the primary JSON field for backwards compat.
	Path    string       `json:"path"`
	Exports []ExportInfo `json:"exports"`

	// PhysicalPath is the symlink-resolved absolute file path. This is
	// the module's *identity*: two ModuleInfo values with the same
	// PhysicalPath refer to the same physical source file, regardless of
	// how they were discovered. This is the key used in s.modules.
	PhysicalPath string `json:"-"`

	// CanonicalID is the pipeline's canonical module ID (used by loader
	// cache and callFunction dispatch). Example: "docparse/services/mcp_tools".
	CanonicalID string `json:"-"`

	// DeclaredPath is the `module X` header as written in source. Used
	// to resolve imports from other local modules. Usually matches
	// CanonicalID; differs under `module_prefix` aliasing.
	DeclaredPath string `json:"-"`

	// File is the parsed AST for the source file. Used for doc comment
	// extraction and by the extract* helpers during registration.
	File *ast.File `json:"-"`

	// Iface is the type-checked interface from the pipeline. Used by
	// callFunction for signature lookup and by the MCP schema generator.
	Iface *iface.Iface `json:"-"`
}

ModuleInfo holds metadata about a loaded AILANG module.

M-SERVEAPI-UNIFY: ModuleInfo is the single source of truth for a local module's identity and all its projections. It is keyed in s.modules by PhysicalPath (symlink-resolved absolute file path) — NOT by the derived `Path` field. Every projection consumer (HTTP routes, OpenAPI, MCP, A2A, function dispatch) reads the field it needs from here, so drift between projection key derivations is structurally impossible.

Non-JSON fields (PhysicalPath, File, Iface) are not serialized in the /modules endpoint response to keep the public API shape unchanged.

type ModulesListResponse

type ModulesListResponse struct {
	Modules []*ModuleInfo `json:"modules"`
	Count   int           `json:"count"`
}

ModulesListResponse is the response for GET /api/_meta/modules.

type RouteEntry

type RouteEntry struct {
	Method     string   // "GET", "POST", etc.
	Path       string   // "/general/v0/general"
	Module     string   // module path
	Function   string   // function name
	IsRaw      bool     // @raw: pass full HttpRequest record instead of parsed args
	IsNowrap   bool     // @nowrap: skip FunctionCallResponse envelope, return raw JSON
	ParamNames []string // parameter names for named JSON binding
	ParamTypes []string // parameter type strings for zero-value padding
}

RouteEntry represents a custom route defined by a @route annotation.

type RouterErrorDetail

type RouterErrorDetail struct {
	// Code is a stable identifier (e.g. "ROUTE_NOT_FOUND"). Clients match
	// on this rather than the human-readable Message.
	Code string `json:"code"`

	// Message is a human-readable description of what went wrong. Always
	// mirrors the flat FunctionCallResponse.Error field for backward compat.
	Message string `json:"message"`

	// Retryable indicates whether the same request could succeed if retried
	// without modification. Router-layer errors are almost always false.
	Retryable bool `json:"retryable"`

	// SuggestedFix is an optional human-readable hint for how to recover.
	// For ROUTE_NOT_FOUND, this is populated with "Did you mean ...?" when
	// a close route match exists. May be empty.
	SuggestedFix string `json:"suggested_fix,omitempty"`

	// AvailableRoutes is an optional list of registered routes that might
	// be relevant to the failed request (e.g. routes sharing a path prefix).
	// Capped at 10 entries. Each entry is formatted as "METHOD /path".
	AvailableRoutes []string `json:"available_routes,omitempty"`
}

RouterErrorDetail is the structured, AI-first error envelope emitted by the serve-api router layer. It's attached to FunctionCallResponse.ErrorDetail so clients can either match on the flat `error` string (backward compat) or on the structured `error_detail.code` field (preferred).

The envelope intentionally mirrors the shape that docparse and other serve-api consumers already use for their own typed errors, so AI agents can use a single error-handling path across all error sources.

type Server

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

Server is the API server that exposes AILANG functions as REST endpoints.

func New

func New(basePath string, cfg Config) *Server

New creates a new API server.

func (*Server) Close

func (s *Server) Close() error

Close shuts down the server and releases resources.

func (*Server) GetEngine

func (s *Server) GetEngine() *embed.Engine

GetEngine returns the underlying embed.Engine for advanced wiring (e.g., connecting FnCaller for stream event handlers).

func (*Server) GetModules

func (s *Server) GetModules() map[string]*ModuleInfo

GetModules returns a copy of the loaded module info map.

func (*Server) LoadModules

func (s *Server) LoadModules(paths []string) error

LoadModules compiles and loads AILANG modules from the given paths. Each path can be a .ail file or a directory (scanned recursively for .ail files).

M-SERVEAPI-UNIFY: both directory and file paths route through the unified loader. Directories go through LoadProject (project-wide single pass); single files go through loadFile, which compiles the file and registers every module from result.Modules via the SAME registerModule write site. There is no longer a separate dep-discovery loop — drift between projection key derivations is structurally impossible.

func (*Server) LoadProject

func (s *Server) LoadProject(ctx context.Context, basePath string) error

LoadProject is the M-SERVEAPI-UNIFY replacement for the per-file loadFile + dep-discovery loop. It walks basePath for .ail files and compiles each one through the pipeline, registering every module in result.Modules via the single-write-site registerModule.

Duplicate work is avoided by tracking compiled physical paths: once a file has been seen in some result.Modules pass, a direct pipeline run for that file is skipped (the loader cache may also short-circuit but this avoids even the cache lookup).

Unlike the old path, LoadProject has ONE registration site and no per-file dedup glue. Projection drift between main path and dep-discovery is structurally impossible because there is no dep-discovery.

func (*Server) Start

func (s *Server) Start() error

Start starts the HTTP server. Blocks until shutdown signal.

func (*Server) StartMCP

func (s *Server) StartMCP() error

StartMCP runs the server as an MCP stdio server (no HTTP). Blocks until done.

Directories

Path Synopsis
Package schema converts AILANG type signatures to JSON Schema objects.
Package schema converts AILANG type signatures to JSON Schema objects.
Package templates provides embedded templates for scaffolding AILANG web apps.
Package templates provides embedded templates for scaffolding AILANG web apps.

Jump to

Keyboard shortcuts

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