PromptKit SDK
Pack-first Go SDK that reduces boilerplate by ~80%
What is the PromptKit SDK?
Section titled “What is the PromptKit SDK?”The SDK uses a pack-first architecture that dramatically simplifies LLM application development:
- 5 lines to hello world - Open a pack, send a message, done
- Pack-first design - Load prompts tested with Arena, compiled with PackC
- Built-in tools - Register handlers with
OnTool, auto JSON serialization - Streaming support - Channel-based streaming with
Stream() - Human-in-the-Loop - Approval workflows for sensitive operations
- Type-safe variables -
SetVar/GetVarwith concurrent access - Observability - EventBus integration for monitoring
Quick Start
Section titled “Quick Start”package main
import ( "context" "fmt" "log"
"github.com/AltairaLabs/PromptKit/sdk")
func main() { // Open a conversation from a pack file conv, err := sdk.Open("./hello.pack.json", "chat") if err != nil { log.Fatal(err) } defer conv.Close()
// Send a message and get a response resp, _ := conv.Send(context.Background(), "Hello!") fmt.Println(resp.Text())}That’s it. Five lines of functional code.
Core API
Section titled “Core API”Opening Conversations
Section titled “Opening Conversations”// Open from a pack file with a specific promptconv, err := sdk.Open("./myapp.pack.json", "assistant")
// Open with optionsconv, err := sdk.Open("./myapp.pack.json", "assistant", sdk.WithModel("gpt-4o"), sdk.WithTemperature(0.7),)Sending Messages
Section titled “Sending Messages”// Simple sendresp, err := conv.Send(ctx, "What's the weather?")fmt.Println(resp.Text())
// Multi-turn conversations (context is maintained)resp1, _ := conv.Send(ctx, "My name is Alice")resp2, _ := conv.Send(ctx, "What's my name?") // "Alice"Template Variables
Section titled “Template Variables”// Set variables for prompt templatesconv.SetVar("user_name", "Alice")conv.SetVar("context", map[string]any{"role": "admin"})
// Get variablesname := conv.GetVar("user_name")
// Bulk operationsconv.SetVars(map[string]any{ "user_name": "Alice", "language": "en",})Tool Handling
Section titled “Tool Handling”Register handlers that the LLM can call:
conv.OnTool("get_weather", func(args map[string]any) (any, error) { city := args["city"].(string)
// Return any JSON-serializable value return map[string]any{ "city": city, "temperature": 22.5, "conditions": "Sunny", }, nil})
// The LLM can now call this toolresp, _ := conv.Send(ctx, "What's the weather in London?")HTTP Tools
Section titled “HTTP Tools”For external API calls:
import "github.com/AltairaLabs/PromptKit/sdk/tools"
conv.OnToolHTTP("stock_price", &tools.HTTPToolConfig{ BaseURL: "https://api.stocks.example.com", Method: "GET", Path: "/v1/price", Headers: map[string]string{"Authorization": "Bearer " + apiKey},})Streaming
Section titled “Streaming”Real-time response streaming:
// Channel-based streamingfor chunk := range conv.Stream(ctx, "Tell me a story") { if chunk.Error != nil { log.Printf("Error: %v", chunk.Error) break } if chunk.Type == sdk.ChunkDone { break } fmt.Print(chunk.Text)}Human-in-the-Loop (HITL)
Section titled “Human-in-the-Loop (HITL)”Approval workflows for sensitive operations:
import "github.com/AltairaLabs/PromptKit/sdk/tools"
conv.OnToolAsync( "process_refund", // Check if approval is needed func(args map[string]any) tools.PendingResult { amount := args["amount"].(float64) if amount > 100 { return tools.PendingResult{ Reason: "high_value", Message: fmt.Sprintf("$%.2f refund requires approval", amount), } } return tools.PendingResult{} // Auto-approve }, // Execute after approval func(args map[string]any) (any, error) { return map[string]any{"status": "completed"}, nil },)
// Handle pending approvalsresp, _ := conv.Send(ctx, "Refund $150 for order #123")for _, pending := range resp.PendingTools() { fmt.Printf("Pending: %s - %s\n", pending.Name, pending.Message)
// Approve or reject result, _ := conv.ResolveTool(pending.ID) // Approve // result, _ := conv.RejectTool(pending.ID, "Not authorized") // Reject}Observability
Section titled “Observability”Monitor events with hooks:
import "github.com/AltairaLabs/PromptKit/sdk/hooks"
// Subscribe to eventsconv.Subscribe(hooks.EventSend, func(e hooks.Event) { fmt.Printf("Sending: %s\n", e.Data["message"])})
conv.Subscribe(hooks.EventToolCall, func(e hooks.Event) { fmt.Printf("Tool called: %s\n", e.Data["tool"])})Error Handling
Section titled “Error Handling”resp, err := conv.Send(ctx, input)if err != nil { switch { case errors.Is(err, sdk.ErrPackNotFound): // Pack file doesn't exist case errors.Is(err, sdk.ErrPromptNotFound): // Prompt ID not in pack case errors.Is(err, sdk.ErrProviderError): // LLM provider error case errors.Is(err, sdk.ErrToolNotRegistered): // Tool handler missing default: log.Printf("Unexpected error: %v", err) }}Examples
Section titled “Examples”Working examples are available in the sdk/examples/ directory:
- hello - Basic conversation in 5 lines
- tools - Tool registration and execution
- streaming - Real-time response streaming
- hitl - Human-in-the-loop approval workflows
Getting Help
Section titled “Getting Help”- Questions: GitHub Discussions
- Issues: Report a Bug
- Examples: SDK Examples
Related Tools
Section titled “Related Tools”- Arena: Test prompts before using them
- PackC: Compile prompts into packs
- Runtime: Extend the SDK with custom providers
- Complete Workflow: See all tools together