An open API service indexing awesome lists of open source software.

https://github.com/dankinsoid/openaimacros

Macros for OpenAI repo
https://github.com/dankinsoid/openaimacros

Last synced: 10 months ago
JSON representation

Macros for OpenAI repo

Awesome Lists containing this project

README

          

# OpenAI Macros

A Swift package that provides macros for seamless integration with OpenAI's function calling API. Instead of manually creating `FunctionDeclaration` structs, simply annotate your Swift functions with `@openAIFunction` and let the macro generate everything automatically.

## Installation

Add this package to your Swift project:

```swift
dependencies: [
.package(url: "https://github.com/yourusername/OpenAIMacros", from: "1.0.0")
]
```

## Quick Start

### 1. Define Your Functions

```swift
import OpenAIMacros

struct WeatherService {
/// Get the current weather for a given location
/// - Parameters:
/// - location: The city and state, e.g. San Francisco, CA
/// - unit: Temperature unit (celsius or fahrenheit)
@openAIFunction
func getCurrentWeather(location: String, unit: TemperatureUnit = .celsius) async throws -> WeatherResponse {
// Your implementation here
return WeatherResponse(temperature: 22.5, unit: unit, description: "Sunny")
}
}

enum TemperatureUnit: String, CaseIterable, Codable {
case celsius
case fahrenheit
}

struct WeatherResponse: Codable {
let temperature: Double
let unit: TemperatureUnit
let description: String
}
```

### 2. Use with OpenAI

```swift
import OpenAI
import OpenAIMacros

let openAI = OpenAI(apiToken: "your-api-key")
let weatherService = WeatherService()

// The macro automatically generates getCurrentWeatherCall
let result = try await openAI.chatsWith(
functions: [weatherService.getCurrentWeatherCall],
query: ChatQuery(
messages: [.user(.init(content: .string("What's the weather like in Boston?")))],
model: "gpt-4o"
)
)

// prints "The current temperature in Boston is 22.5°C."
print(result.choices[0].message.content ?? "No response")
```

## What the Macro Generates

The `@openAIFunction` macro automatically generates:

1. **Parameter Struct**: `GetCurrentWeatherParameters` with proper `Codable` support
2. **Function Wrapper**: `getCurrentWeatherCall` that bridges OpenAI ↔ Swift
3. **JSON Schema**: Complete parameter validation with types and descriptions
4. **Custom Decoder**: Handles default values (e.g., `unit` parameter is optional in JSON)

### Generated Code Example

```swift
// Generated by @openAIFunction
struct GetCurrentWeatherParameters: Decodable {
let location: String
let unit: TemperatureUnit

init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: CodingKeys.self)
self.location = try container.decode(String.self, forKey: .location)
self.unit = try container.decodeIfPresent(TemperatureUnit.self, forKey: .unit) ?? .celsius
}

private enum CodingKeys: String, CodingKey {
case location
case unit
}
}

var getCurrentWeatherCall: OpenAIFunctionWrapper {
OpenAIFunctionWrapper(
difinition: ChatQuery.ChatCompletionToolParam.FunctionDefinition(
name: "getCurrentWeather",
description: "Get the current weather for a given location",
parameters: AnyJSONSchema(fields: [
.type(.object),
.properties([
"location": AnyJSONSchema.forType(String.self, description: "The city and state, e.g. San Francisco, CA"),
"unit": AnyJSONSchema.forType(TemperatureUnit.self, description: "Temperature unit (celsius or fahrenheit)")
]),
.required(["location"])
])
)
) { [self] decoder, data, encoder in
let parameters = try decoder.decode(GetCurrentWeatherParameters.self, from: data)
let result = try await getCurrentWeather(location: parameters.location, unit: parameters.unit)
return try encoder.encode(result)
}
}
```

## Features

### ✅ **Automatic Code Generation**
- Parameter structs with proper `Codable` support
- JSON schema generation with type validation
- Function wrappers that handle the OpenAI ↔ Swift bridge

### ✅ **Smart Type Detection**
- **Primitives**: `String`, `Int`, `Double`, `Bool`
- **Enums**: `CaseIterable` types → JSON schema with enum values
- **Collections**: `Array`, `Dictionary`, `Set` → appropriate JSON types
- **Custom Types**: `Codable` structs/classes → JSON objects
- **Optionals**: Proper handling of optional parameters

### ✅ **Default Values Support**
- Parameters with defaults are optional in JSON
- Custom decoders handle missing values gracefully
- `nil` defaults are treated as required parameters

### ✅ **Documentation Integration**
- Function descriptions from doc comments
- Parameter descriptions from `@param` documentation
- Automatic cleanup of doc comment markers

### ✅ **Async/Throws Support**
- Detects `async` and `throws` in function signatures
- Generates appropriate `await` and `try` keywords
- Full support for async throwing functions

### ✅ **Complete Function Calling Workflow**
- `chatsWith()` method handles the entire OpenAI function calling flow
- Recursive execution of multiple function calls
- Proper error handling and result processing

## Advanced Usage

### Multiple Functions (TODO - Future Version)

```swift
// TODO: This pattern will be supported in a future version
struct MyAPI {
@openAIFunction
func getWeather(location: String) async throws -> WeatherResponse {
// Implementation
}

@openAIFunction
func searchRestaurants(location: String, cuisine: String? = nil) async throws -> [Restaurant] {
// Implementation
}

// TODO: Auto-generate this with @OpenAIFunctionsCollection macro
var allFunctions: [OpenAIFunctionWrapper] {
[getWeatherCall, searchRestaurantsCall]
}
}

// Use all functions
let api = MyAPI()
let result = try await openAI.chatsWith(
functions: api.allFunctions,
query: query
)
```

**Current workaround**: For now, you need to manually create the `allFunctions` array or call each function wrapper individually.

### Error Handling

```swift
do {
let result = try await openAI.chatsWith(functions: functions, query: query)
// Handle result
} catch let error as UnknownFunctionCall {
print("Unknown function: \(error.functionName)")
} catch let error as InvalidString {
print("String conversion error: \(error.message)")
} catch {
print("Other error: \(error)")
}
```

## Requirements

- Swift 5.9+ (for macro support)
- macOS 10.15+, iOS 13+, tvOS 13+, watchOS 6+
- [MacPaw's OpenAI Swift library](https://github.com/MacPaw/OpenAI)

## What's Next

This is the first version of OpenAI Macros. Future versions may include:
- `@OpenAIFunctionsCollection` macro for automatic function grouping
- Support for more complex parameter types
- Integration with other OpenAI Swift libraries
- Enhanced error handling and debugging

## License

MIT License - see LICENSE file for details.