Releases: SwiftedMind/SwiftAgent
0.8.0
Added
- Tool schema JSON export:
DecodableTool.jsonSchema(prettyPrinted:)now emits a function-call compatible JSON schema (name, description, parameters) in compact or pretty form for session schema tools. - Generation error codes:
GenerationErrorexposeserrorCodewith concise identifiers for each case, making it easier to log and triage failures. - Anthropic adapter support: Added
AnthropicSessionwith streaming, tool calls, structured outputs, and thinking support powered by SwiftAnthropic. - Example app: Added an Anthropic playground alongside the OpenAI demo.
- HTTP fixture recorder: Added
HTTPReplayRecorderandHTTPClientInterceptorshooks to capture request/response payloads (including streamingtext/event-stream) and print paste-ready Swift fixtures for unit tests. - AgentRecorder CLI: Added a macOS command-line tool (
AgentRecorder) that runs recording scenarios and prints paste-ready fixtures viaHTTPReplayRecorder.
Enhanced
Fixed
- Breaking Change: Renamed
SessionSchema.decodableToolstoSessionSchema.tools - Stable GeneratedContent JSON: Added
GeneratedContent.stableJsonStringto reserialize payloads with sorted keys, improving cache hit rates while Apple addresses FB20745786. - Cancellation normalized: All providers now map task and network cancellations to
GenerationError.cancelled, avoiding generic errors when generations end early.
0.7.2
Changed
- Bumped
swift-syntaxversion to600.0.0..<603.0.0
0.7.1
Fixed
- Fixed some imports in the example app
0.7.0
Breaking Changes
-
OpenAISession Replaces ModelSession:
ModelSession.openAI(...)has been removed. Create sessions withOpenAISessionand pass tools as variadic parameters.OpenAISessionis@Observable, so view code can observe transcripts directly.// Before let session = ModelSession.openAI( tools: [WeatherTool(), CalculatorTool()], instructions: "You are a helpful assistant.", apiKey: "sk-..." ) // Now let session = OpenAISession( tools: WeatherTool(), CalculatorTool(), instructions: "You are a helpful assistant.", apiKey: "sk-..." )
-
FoundationModels Tool Adoption: Tools now conform directly to the FoundationModels
Toolprotocol and mocks adoptMockableTool. Rename anyAgentToolorSwiftAgentToolconformances.// Before struct WeatherTool: AgentTool { ... } struct WeatherToolMock: MockableAgentTool { ... } // Now struct WeatherTool: Tool { ... } // No custom protocol needed anymore struct WeatherToolMock: MockableTool { ... }
-
StructuredOutput Protocol Required for Guided Responses: Guided generations now require types to conform to
StructuredOutputwith an embeddedSchema. Replace plain@Generabledata transfer objects that were passed directly intorespond(generating:).// Before @Generable struct WeatherReport { let temperature: Double } let response = try await session.respond( to: "Describe the weather", generating: WeatherReport.self ) // Now struct WeatherReport: StructuredOutput { static let name: String = "weatherReport" @Generable struct Schema { let temperature: Double } } let response = try await session.respond( to: "Describe the weather", generating: WeatherReport.self )
-
Session Schema Macro Supersedes PromptContext and Tool Resolver: The
PromptContext,toolResolver, andtranscript.decoded(using:)helpers have been removed. Declare an@SessionSchemawith@Tool,@Grounding, and@StructuredOutputwrappers and decode transcripts through it.// Before enum PromptContext: SwiftAgent.PromptContext { ... } let toolResolver = session.transcript.toolResolver(for: tools) // Now @SessionSchema struct SessionSchema { @Tool var weatherTool = WeatherTool() @Grounding(Date.self) var currentDate // Groundings replace "PromptContext" @Grounding([VectorSearchResult].self) var VectorSearchResults } let sessionSchema = SessionSchema() let session = OpenAISession(schema: sessionSchema, ...) for entry in try sessionSchema.decode(session.transcript) { // Inspect prompts, groundings, and tool runs }
-
Simulation Workflow Overhauled:
ModelSession.simulateResponsehas been removed. UseSimulatedSessionwith aSessionSchema,MockableToolwrappers, andSimulationConfigurationto define scripted turns.// Before let response = try await session.simulateResponse( to: "What's the weather like in San Francisco?", generations: [ .toolRun(tool: WeatherToolMock(tool: WeatherTool())), .response(content: "Sunny!") ] ) // Now let session = SimulatedSession( schema: SessionSchema(), instructions: "You are a helpful assistant.", configuration: SimulationConfiguration(defaultGenerations: [ .toolRun(tool: WeatherToolMock(tool: WeatherTool())), .response(text: "Sunny!") ]) ) let response = try await session.respond(to: "What's the weather like in San Francisco?")
Added
-
Streaming Responses and Structured Outputs:
streamResponseyields snapshots that include partial content, live transcripts, and structured output projections.let stream = try session.streamResponse( to: "Summarize yesterday's revenue", generating: \.weatherReport ) for try await snapshot in stream { if let content = snapshot.content { print(content) } print(snapshot.transcript) }
-
Recoverable Tool Rejections: Tools can throw
ToolRunRejectionwith rich@Generablepayloads so the agent can adjust without aborting the turn. The rejection surfaces onToolRun.rejectionfor diagnostics and retry logic.throw ToolRunRejection( reason: "Customer not found", content: CustomerLookupRejectionDetails( issue: "customerNotFound", customerId: arguments.customerId, suggestions: ["Ask the user to confirm the identifier"] ) )
-
Proxy Configuration and Per-Turn Authorization: Configure
OpenAIConfiguration.proxy(through:)to route traffic through your backend and wrap calls withsession.withAuthorization(token:perform:)to attach per-turn credentials.let configuration = OpenAIConfiguration.proxy(through: URL(string: "https://api.example.com/proxy")!) let session = OpenAISession( instructions: "You are a helpful assistant.", configuration: configuration ) let token = try await backend.issueTurnToken(for: userId) let response = try await session.withAuthorization(token: token) { try await session.respond(to: "Draft the status update.") }
Fixed
- Rejection Report Validation: Tool output decoding errors now propagate unless the payload matches the recoverable
ToolRunRejectionreport structure, preventing silent failures in custom tools.
0.6.0
Added
-
Session Management Methods: Added
clearTranscript()andresetTokenUsage()methods to ModelSession for better session lifecycle management.// Clear the conversation transcript while keeping the session configuration session.clearTranscript() // Reset cumulative token usage counter session.resetTokenUsage() // Both methods can be used independently or together session.clearTranscript() session.resetTokenUsage()
-
Token Usage Tracking and Reporting: Added comprehensive token usage monitoring across all AI interactions with both per-response and session-level tracking.
let response = try await session.respond(to: "What's the weather?") // Access aggregated token usage from the individual response if let usage = response.tokenUsage { print("Response tokens used: \(usage.totalTokens ?? 0)") print("Response input tokens: \(usage.inputTokens ?? 0)") print("Response output tokens: \(usage.outputTokens ?? 0)") print("Response cached tokens: \(usage.cachedTokens ?? 0)") print("Response reasoning tokens: \(usage.reasoningTokens ?? 0)") } // Access cumulative token usage across the entire session print("Session total tokens: \(session.tokenUsage.totalTokens ?? 0)") print("Session input tokens: \(session.tokenUsage.inputTokens ?? 0)") print("Session output tokens: \(session.tokenUsage.outputTokens ?? 0)") // Session token usage updates in real-time during streaming responses // Perfect for @Observable integration in SwiftUI for live usage monitoring
Fixed
- Transcript ID Handling: Fixed issue where transcript IDs were not properly converting back to original OpenAI IDs, removing unnecessary manual addition of "fc_" prefix from function call IDs.
- Tool Output Status Tracking: Added missing
statusfield toToolOutputinAgentTranscriptfor better tool execution tracking and consistency. - JSON Encoding Determinism: Enabled sorted keys in OpenAI JSON encoder to ensure consistent property ordering in tool schemas, preventing cache misses and improving prompt caching effectiveness.
0.5.0
Added
-
Simulated Sessions: Introduced the
SimulatedSessionmodule for testing and development without API calls. The simulation system includes:simulateResponsemethods that mirror the standardrespondAPIMockableAgentToolprotocol for creating mock tool calls and outputsSimulatedGenerationenum supporting tool runs, reasoning, and text or structured responses, simulating model generations- Complete transcript compatibility - simulated responses work on the real transcript object, guaranteeing full compatibility with the actual agent
- Zero API costs during development and testing
import OpenAISession import SimulatedSession // Create mockable tool wrappers struct WeatherToolMock: MockableAgentTool { var tool: WeatherTool func mockArguments() -> WeatherTool.Arguments { /* mock data */ } func mockOutput() async throws -> WeatherTool.Output { /* mock results */ } } // Create an OpenAI session as normal let session = ModelSession.openAI( tools: [WeatherTool()], // Your actual tool; the mockable tool will be used below instructions: "You are a helpful assistant.", apiKey: "sk-...", ) // And then use simulateResponse instead of respond let response = try await session.simulateResponse( to: "What's the weather?", generations: [ .toolRun(tool: WeatherToolMock(tool: WeatherTool())), .response(content: "It's sunny!") ] )
-
The
PromptContextprotocol has been replaced with a generic struct wrapper that provides both user-written input and app or SDK generated context data (like link previews or vector search results). User types now conform toPromptContextSourceinstead ofPromptContext:// Define your context source enum ContextSource: PromptContextSource { case vectorSearchResult(String) case searchResults([String]) } // Create a session with context support and pass the context source let session = ModelSession.openAI(tools: tools, context: ContextSource.self, apiKey: "sk-...") // Respond with context - user input and context are kept separated in the transcript let response = try await session.respond( to: "What are the key features of SwiftUI?", supplying: [ .vectorSearchResult("SwiftUI declarative syntax..."), .searchResults("Apple's official SwiftUI documentation...") ] ) { input, context in PromptTag("context", items: context.sources) input }
-
Link Previews in Prompt Context: The new
PromptContextstruct includeslinkPreviewsthat can automatically fetch and include metadata from URLs in user inputs -
OpenAI Generation Configuration: The new
OpenAIGenerationOptionstype provides access to OpenAI API parameters including:include- Additional outputs like reasoning or logprobsallowParallelToolCalls- Control parallel tool executionreasoning- Configuration for reasoning-capable modelssafetyIdentifier- Misuse detection identifierserviceTier- Request priority and throughput controltoolChoice- Fine-grained tool selection controltopLogProbs- Token probability informationtopP- Alternative sampling methodtruncation- Context window handling- And more OpenAI-specific options
Changed
-
Breaking Change: Restructured the products in the SDK. Each provider now has its own product, e.g.
OpenAISessionimport OpenAISession -
Breaking Change: Renamed nearly all the types in the SDK to close align with FoundationModels types.
Agentis nowModelSession, andOpenAIAgentis nowOpenAISession:import OpenAISession // Create an OpenAI session through the ModelSession type let session = ModelSession.openAI( tools: [WeatherTool(), CalculatorTool()], instructions: "You are a helpful assistant.", apiKey: "sk-...", )
-
Breaking Change: Replaced the generic
GenerationOptionsstruct with adapter-specific generation options. Each adapter now defines its ownGenerationOptionstype as an associated type, providing better type safety and access to adapter-specific parameters:// Before let options = GenerationOptions(temperature: 0.7, maximumResponseTokens: 1000) let response = try await agent.respond(to: prompt, options: options) // Now let options = OpenAIGenerationOptions(temperature: 0.7, maxOutputTokens: 1000) let response = try await session.respond(to: prompt, options: options)
0.4.1
Fixed
- Agent Text Response Content Formatting: Fixed an issue with the agent's text response content formatting that could cause malformed responses
- Tool Resolution: Fixed a critical bug where tools would never be resolved due to mismatched IDs, ensuring proper tool call execution
- Tool Resolution Logging: Improved logging around tool resolution to better debug tool call issues
Enhanced
- Collection Protocol Conformance: Made
AgentTranscriptandAgentTranscript.ToolCallsconform to theCollectionprotocol, making it easier to access theirentriesandcallsproperties and work with them using standard Swift collection methods - Logging System: Added general logging methods and enhanced tool resolution logging for better debugging and monitoring
- Example App: Added a proper, modern example app with native SwiftUI design that demonstrates the SDK's capabilities
Other
- Code Cleanup: Minor code cleanup and formatting improvements across the codebase
- UI Modernization: Redesigned example app UI with new tools and modern SwiftUI patterns
0.4.0
Breaking Changes
-
Renamed
ProvidertoAdapter: The core abstraction for AI model integrations has been renamed fromProvidertoAdapterfor better clarity. Update all references to use the new naming:// Before let agent = Agent<OpenAIProvider, Context>() // Now let agent = Agent<OpenAIAdapter, Context>()
-
Renamed
TranscripttoAgentTranscript: To avoid naming conflicts with FoundationModels, theTranscripttype has been renamed toAgentTranscript:// Before public var transcript: Transcript // Now public var transcript: AgentTranscript<Adapter.Metadata, Context>
Added
-
Prompt Context System: Introduced a new
PromptContextprotocol that enables separation of user input from contextual information (such as vector embeddings or retrieved documents). This provides cleaner transcript organization and better prompt augmentation:enum PromptContext: SwiftAgent.PromptContext { case vectorEmbedding(String) case documentContext(String) } let agent = OpenAIAgent(supplying: PromptContext.self, tools: tools) // User input and context are now separated in the transcript let response = try await agent.respond( to: "What is the weather like?", supplying: [.vectorEmbedding("relevant weather data")] ) { input, context in PromptTag("context", items: context) input }
-
Tool Resolver: Added a powerful type-safe tool resolution system that combines tool calls with their outputs. The
ToolResolverenables compile-time access to tool arguments and outputs:// Define a resolved tool run enum enum ResolvedToolRun { case getFavoriteNumbers(AgentToolRun<GetFavoriteNumbersTool>) } // Tools must implement the resolve method func resolve(_ run: AgentToolRun<GetFavoriteNumbersTool>) -> ResolvedToolRun { .getFavoriteNumbers(run) } // Use the tool resolver in your UI code let toolResolver = agent.transcript.toolResolver(for: tools) for entry in agent.transcript.entries { if case let .toolCalls(toolCalls) = entry { for toolCall in toolCalls.calls { let resolvedTool = try toolResolver.resolve(toolCall) switch resolvedTool { case let .getFavoriteNumbers(run): print("Count:", run.arguments.count) if let output = run.output { print("Numbers:", output.numbers) } } } } }
-
Convenience Initializers: Added streamlined initializers that reduce generic complexity. The new
OpenAIAgenttypealias and convenience initializers make agent creation more ergonomic:// Simplified initialization with typealias let agent = OpenAIAgent(supplying: PromptContext.self, tools: tools) // No context needed let agent = OpenAIAgent(tools: tools) // Even simpler for basic usage let agent = OpenAIAgent()
Enhanced
- AgentTool Protocol: Extended the
AgentToolprotocol with an optionalResolvedToolRunassociated type to support the new tool resolver system - Type Safety: Improved compile-time type safety for tool argument and output access through the tool resolver
- Transcript Organization: Better separation of concerns in transcript entries, with user input and context clearly distinguished
Migration Guide
- Update Provider references: Replace all instances of
ProviderwithAdapterin your code - Update Transcript references: Replace
TranscriptwithAgentTranscriptwhere needed - Consider adopting PromptContext: If you're currently building prompts with embedded context outside the agent, consider migrating to the new
PromptContextsystem for cleaner separation - Adopt Tool Resolver: For better type safety in UI code that displays tool runs, implement the
resolvemethod in your tools and use the transcript'stoolResolver - Use convenience initializers: Simplify your agent initialization code using the new
OpenAIAgenttypealias and convenience initializers
0.3.0
Additions
- Added network logging to debug all network requests and responses
- Added support for optional properties in tools (Apple's
GenerationSchematype doesn't quite conform to what OpenAI expects)
0.2.0
Additions
- Added
PromptBuilderresult builder that works pretty much identically to the one inFoundationModels