Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
63 changes: 63 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ print(response.content)
- [x] [llama.cpp](https://github.com/ggml-org/llama.cpp) (GGUF models)
- [x] Ollama [HTTP API](https://github.com/ollama/ollama/blob/main/docs/api.md)
- [x] Anthropic [Messages API](https://docs.claude.com/en/api/messages)
- [x] Google [Gemini API](https://ai.google.dev/api/generate-content)
- [x] OpenAI [Chat Completions API](https://platform.openai.com/docs/api-reference/chat)
- [x] OpenAI [Responses API](https://platform.openai.com/docs/api-reference/responses)

Expand Down Expand Up @@ -228,6 +229,68 @@ let response = try await session.respond {
}
```

### Google Gemini

Uses the [Gemini API](https://ai.google.dev/api/generate-content) with Gemini models:

```swift
let model = GeminiLanguageModel(
apiKey: ProcessInfo.processInfo.environment["GEMINI_API_KEY"]!,
model: "gemini-2.5-flash"
)

let session = LanguageModelSession(model: model, tools: [WeatherTool()])
let response = try await session.respond {
Prompt("What's the weather like in Tokyo?")
}
```

Gemini models use an internal ["thinking process"](https://ai.google.dev/gemini-api/docs/thinking)
that improves reasoning and multi-step planning.
You can configure how much Gemini should "think" using the `thinking` parameter:

```swift
// Enable thinking
var model = GeminiLanguageModel(
apiKey: apiKey,
model: "gemini-2.5-flash",
thinking: true /* or `.dynamic` */,
)

// Set an explicit number of tokens for its thinking budget
model.thinking = .budget(1024)

// Revert to default configuration without thinking
model.thinking = false /* or `.disabled` */
```

Gemini supports [server-side tools](https://ai.google.dev/gemini-api/docs/google-search)
that execute transparently on Google's infrastructure:

```swift
let model = GeminiLanguageModel(
apiKey: apiKey,
model: "gemini-2.5-flash",
serverTools: [
.googleMaps(latitude: 35.6580, longitude: 139.7016) // Optional location
]
)
```

**Available server tools**:

- `.googleSearch`
Grounds responses with real-time web information
- `.googleMaps`
Provides location-aware responses
- `.codeExecution`
Generates and runs Python code to solve problems
- `.urlContext`
Fetches and analyzes content from URLs mentioned in prompts

> [!TIP]
> Gemini server tools are not available as client tools (`Tool`) for other models.

### Ollama

Run models locally via Ollama's [HTTP API](https://github.com/ollama/ollama/blob/main/docs/api.md):
Expand Down
30 changes: 24 additions & 6 deletions Sources/AnyLanguageModel/GenerationSchema.swift
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,6 @@ import struct Foundation.Decimal
/// Generation schemas guide the output of a ``SystemLanguageModel`` to deterministically
/// ensure the output is in the desired format.
public struct GenerationSchema: Sendable, Codable, CustomDebugStringConvertible {

// MARK: - Structure

indirect enum Node: Sendable, Codable {
case object(ObjectNode)
case array(ArrayNode)
Expand Down Expand Up @@ -44,7 +41,12 @@ public struct GenerationSchema: Sendable, Codable, CustomDebugStringConvertible
try propsContainer.encode(node, forKey: DynamicCodingKey(stringValue: name)!)
}
try container.encode(Array(obj.required), forKey: .required)
try container.encode(false, forKey: .additionalProperties)

// Check userInfo to see if additionalProperties should be omitted
let shouldOmit = encoder.userInfo[GenerationSchema.omitAdditionalPropertiesKey] as? Bool ?? false
if !shouldOmit {
try container.encode(false, forKey: .additionalProperties)
}

case .array(let arr):
try container.encode("array", forKey: .type)
Expand Down Expand Up @@ -201,8 +203,6 @@ public struct GenerationSchema: Sendable, Codable, CustomDebugStringConvertible
}
}

// MARK: - Properties

let root: Node
private var defs: [String: Node]

Expand Down Expand Up @@ -774,3 +774,21 @@ extension GenerationSchema {
}
}
}

// MARK: - CodingUserInfoKey

extension GenerationSchema {
/// A key used in the encoder's `userInfo` dictionary to control whether
/// the `additionalProperties` field should be omitted from the encoded output.
///
/// Set this to `true` to omit `additionalProperties` from object schemas.
/// Defaults to `false` (includes `additionalProperties`) if not specified.
///
/// Example:
/// ```swift
/// let encoder = JSONEncoder()
/// encoder.userInfo[GenerationSchema.omitAdditionalPropertiesKey] = true
/// let data = try encoder.encode(schema)
/// ```
static let omitAdditionalPropertiesKey = CodingUserInfoKey(rawValue: "GenerationSchema.omitAdditionalProperties")!
}
Loading