Ollama Swift Client

repository·main·Indexed 19 days ago

https://github.com/mattt/ollama-swift

A Swift library for interacting with the Ollama API to integrate local LLM capabilities into macOS applications. It supports text generation, streaming responses, chat conversations, tool calling for external functions, and vector embeddings. The client also provides model management utilities to list, show, pull, push, create, copy, and delete models, as well as the ability to retrieve the Ollama server version.

Tokens
3.7K
Snippets
17
Records
20
Agent score
16%

What's inside ollama-swift

  1. Use thinking models for reasoning

    main

    Models like deepseek-r1 support a "thinking" mode where they output their reasoning process. Enable this by setting think: true in generate or chat calls. You can access the reasoning via the thinking property on the response.

    // Generate with thinking enabled
    let response = try await client.generate(
        model: "deepseek-r1:8b",
        prompt: "What is 17 * 23? Show your work.",
        think: true
    )
    
    print("Thinking: \(response.thinking ?? "None")")
    print("Answer: \(response.response)")
  2. Create and use tools for function calling

    main

    Tools allow models to interact with external functions. To use tools:

    1. Define a Tool with a name, description, and parameters (the properties object).
    2. Provide the tool(s) in the tools array when calling chat or chatStream.
    3. Handle toolCalls in the response by executing your local implementation and adding the result back to the conversation using .tool(result).
    // Define a tool
    let weatherTool = Tool<WeatherInput, WeatherOutput>(
        name: "get_current_weather",
        description: "Get the current weather for a city",
        parameters: [
            "city": [
                "type": "string",
                "description": "The city to get weather for"
            ]
        ],
        required: ["city"]
    ) { input async throws -> WeatherOutput in
        return WeatherOutput(temperature: 18.5, conditions: "cloudy")
    }
    
    // Use the tool in chat
    let response = try await client.chat(
        model: "llama3.1",
        messages: messages,
        tools: [weatherTool]
    )
  3. Install Ollama Swift Client via Swift Package Manager

    main

    To add ollama-swift to your project, add the following dependency to your Package.swift file. This library requires Swift 5.7+, macOS 13+, and a running instance of Ollama.

    .package(url: "https://github.com/mattt/ollama-swift.git", from: "1.8.0")
  4. Initialize the Ollama Client

    main

    You can use a default client configured for http://localhost:11434 or create a custom client by specifying a host URL and a user agent string.

    import Ollama
    
    // Use the default client (http://localhost:11434)
    let client = Client.default
    
    // Or create a custom client
    let customClient = Client(host: URL(string: "http://your-ollama-host:11434")!, userAgent: "MyApp/1.0")
  5. Requirements for Ollama Swift Client

    main

    Before using this library, ensure your environment meets the following requirements:

    • Swift: version 5.7 or higher
    • macOS: version 13 or higher
    • Ollama: Must be installed and running on your system.
  6. Manage model memory with `keepAlive`

    main

    The keepAlive parameter controls how long a model remains loaded in the Ollama server's memory. This helps balance between immediate response times and system resource usage.

    Available options:

    • .default: Use server default.
    • .none: Unload model immediately after the request.
    • .seconds(Int): Keep loaded for N seconds.
    • .minutes(Int): Keep loaded for N minutes.
    • .hours(Int): Keep loaded for N hours.
    • .forever: Keep loaded indefinitely.

    Note: Zero durations are treated as .none; negative durations are treated as .forever.

    // Keep model loaded for 10 minutes
    let response = try await client.generate(
        model: "llama3.2",
        prompt: "Hello!",
        keepAlive: .minutes(10)
    )
  7. Stream text generation with `generateStream()`

    main

    To receive responses in real-time, use generateStream. This returns an async sequence of chunks that you can iterate over to process the response as it arrives.

    do {
        let stream = try await client.generateStream(
            model: "llama3.2",
            prompt: "Tell me a joke about Swift programming.",
            options: [
                "temperature": 0.7,
                "max_tokens": 100
            ]
        )
    
        var fullResponse = ""
        for try await chunk in stream {
            // Process each chunk of the response as it arrives
            print(chunk.response, terminator: "")
            fullResponse += chunk.response
        }
        print("\nFull response: \(fullResponse)")
    } catch {
        print("Error: \(error)")
    }
  8. Manage models (list, show, pull, push)

    main

    The client provides methods to manage the local Ollama model library:

    • listModels(): Returns a list of available models.
    • showModel(name): Returns detailed information (Modelfile, parameters, template) for a specific model.
    • pullModel(name): Downloads a model from the Ollama library.
    • pushModel(name): Uploads a model to a namespace.
    // List models
    let models = try await client.listModels()
    
    // Pull a model
    let success = try await client.pullModel("llama3.2")
  9. Stream chat responses with `chatStream()`

    main

    Use chatStream to get real-time partial completions for chat conversations. This is useful for UI elements that show text being typed out. You can also check for toolCalls or the done flag within the stream chunks.

    do {
        let stream = try await client.chatStream(
            model: "llama3.2",
            messages: [
                .system("You are a helpful assistant."),
                .user("Write a short poem about Swift programming.")
            ]
        )
    
        var fullContent = ""
        for try await chunk in stream {
            // Process each chunk of the message as it arrives
            if let content = chunk.message.content {
                print(content, terminator: "")
                fullContent += content
            }
        }
        print("\nComplete poem: \(fullContent)")
    } catch {
        print("Error: \(error)")
    }
  10. Generate text with `generate()`

    main

    Use the generate method to produce a single text response from a model using a prompt. You can provide optional parameters like options (e.g., temperature, max_tokens) and keepAlive to manage model memory.

    do {
        let response = try await client.generate(
            model: "llama3.2",
            prompt: "Tell me a joke about Swift programming.",
            options: [
                "temperature": 0.7,
                "max_tokens": 100
            ],
            keepAlive: .minutes(10)  // Keep model loaded for 10 minutes
        )
        print(response.response)
    } catch {
        print("Error: \(error)")
    }
  11. Chat with a model using `chat()`

    main

    Use the chat method to generate completions based on a conversation history. Messages are provided as an array of Chat.Message objects (e.g., .system, .user, .assistant).

    do {
        let response = try await client.chat(
            model: "llama3.2",
            messages: [
                .system("You are a helpful assistant."),
                .user("In which city is Apple Inc. located?")
            ],
            keepAlive: .minutes(10)  // Keep model loaded for 10 minutes
        )
        print(response.message.content)
    } catch {
        print("Error: \(error)")
    }
  12. Generate embeddings with `embed()`

    main

    Use embed() to generate vector embeddings for text. You can pass a single string via input or an array of strings via inputs for batch processing.

    do {
        let response = try await client.embed(
            model: "llama3.2",
            input: "Here is an article about llamas..."
        )
        print("Embeddings: \(response.embeddings)")
    } catch {
        print("Error: \(error)")
    }