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
59 changes: 59 additions & 0 deletions .github/workflows/deploy-docs.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
name: Deploy Docs

on:
push:
branches: [main]
paths:
- 'docs/**'
- 'package.json'

workflow_dispatch:

permissions:
contents: read
pages: write
id-token: write

concurrency:
group: pages
cancel-in-progress: false

jobs:
build:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4
with:
fetch-depth: 0

- name: Setup Node
uses: actions/setup-node@v4
with:
node-version: 22
cache: npm

- name: Install dependencies
run: npm ci

- name: Build docs
run: npm run docs:build

- name: Setup Pages
uses: actions/configure-pages@v5

- name: Upload artifact
uses: actions/upload-pages-artifact@v3
with:
path: docs/.vitepress/dist

deploy:
environment:
name: github-pages
url: ${{ steps.deployment.outputs.page_url }}
needs: build
runs-on: ubuntu-latest
steps:
- name: Deploy to GitHub Pages
id: deployment
uses: actions/deploy-pages@v4
5 changes: 5 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -16,3 +16,8 @@ osaurus/

# Reference implementations (not part of build)
clone/

# VitePress / Node
node_modules/
docs/.vitepress/dist/
docs/.vitepress/cache/
108 changes: 108 additions & 0 deletions docs/.vitepress/config.mts
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
import { defineConfig } from 'vitepress'

export default defineConfig({
title: 'Conduit',
description: 'Unified Swift SDK for local and cloud LLM inference',
base: '/Conduit/',

head: [
['link', { rel: 'icon', href: '/Conduit/conduit-logo.svg' }],
],

themeConfig: {
logo: '/conduit-logo.svg',

nav: [
{ text: 'Guide', link: '/guide/getting-started' },
{ text: 'Providers', link: '/providers/' },
{
text: 'v0.3',
items: [
{ text: 'Changelog', link: 'https://github.com/christopherkarani/Conduit/releases' },
{ text: 'GitHub', link: 'https://github.com/christopherkarani/Conduit' },
],
},
],

sidebar: {
'/guide/': [
{
text: 'Essentials',
items: [
{ text: 'Getting Started', link: '/guide/getting-started' },
{ text: 'Architecture', link: '/guide/architecture' },
],
},
{
text: 'Generation',
items: [
{ text: 'Streaming', link: '/guide/streaming' },
{ text: 'Structured Output', link: '/guide/structured-output' },
{ text: 'Tool Calling', link: '/guide/tool-calling' },
],
},
{
text: 'Services',
items: [
{ text: 'Chat Session', link: '/guide/chat-session' },
{ text: 'Model Management', link: '/guide/model-management' },
],
},
{
text: 'Reference',
items: [
{ text: 'Error Handling', link: '/guide/error-handling' },
{ text: 'Platform Support', link: '/guide/platform-support' },
{ text: 'SwiftAgents Integration', link: '/guide/swift-agents-integration' },
],
},
],

'/providers/': [
{
text: 'Overview',
items: [
{ text: 'Providers Overview', link: '/providers/' },
],
},
{
text: 'Cloud Providers',
items: [
{ text: 'Anthropic', link: '/providers/anthropic' },
{ text: 'OpenAI', link: '/providers/openai' },
{ text: 'HuggingFace', link: '/providers/huggingface' },
{ text: 'Kimi', link: '/providers/kimi' },
{ text: 'MiniMax', link: '/providers/minimax' },
],
},
{
text: 'Local Providers',
items: [
{ text: 'MLX', link: '/providers/mlx' },
{ text: 'Foundation Models', link: '/providers/foundation-models' },
{ text: 'CoreML', link: '/providers/coreml' },
{ text: 'Llama', link: '/providers/llama' },
],
},
],
},

socialLinks: [
{ icon: 'github', link: 'https://github.com/christopherkarani/Conduit' },
],

editLink: {
pattern: 'https://github.com/christopherkarani/Conduit/edit/main/docs/:path',
text: 'Edit this page on GitHub',
},

search: {
provider: 'local',
},

footer: {
message: 'Released under the MIT License.',
copyright: 'Copyright 2024-present Christopher Karani',
},
},
})
4 changes: 4 additions & 0 deletions docs/.vitepress/theme/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
import DefaultTheme from 'vitepress/theme'
import './style.css'

export default DefaultTheme
41 changes: 41 additions & 0 deletions docs/.vitepress/theme/style.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
:root {
--vp-c-brand-1: #F05138;
--vp-c-brand-2: #e6432b;
--vp-c-brand-3: #cc3520;
--vp-c-brand-soft: rgba(240, 81, 56, 0.14);
}

.dark {
--vp-c-brand-1: #F4735E;
--vp-c-brand-2: #F05138;
--vp-c-brand-3: #e6432b;
--vp-c-brand-soft: rgba(240, 81, 56, 0.16);
}

:root {
--vp-home-hero-name-color: transparent;
--vp-home-hero-name-background: -webkit-linear-gradient(
120deg,
#F05138 30%,
#e6432b
);

--vp-home-hero-image-background-image: linear-gradient(
-45deg,
#F05138 50%,
#f4735e 50%
);
--vp-home-hero-image-filter: blur(44px);
}

@media (min-width: 640px) {
:root {
--vp-home-hero-image-filter: blur(56px);
}
}

@media (min-width: 960px) {
:root {
--vp-home-hero-image-filter: blur(68px);
}
}
162 changes: 162 additions & 0 deletions docs/guide/architecture.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,162 @@
# Architecture

Understand Conduit's protocol hierarchy, type system, and design patterns.

## Overview

Conduit is built around a set of protocols that define provider capabilities. Every provider is an actor for thread-safe concurrent use, and all public types are `Sendable`. This architecture lets you write provider-agnostic app code and swap providers by changing a single initializer.

## Protocol Hierarchy

All providers conform to one or more of these protocols:

- **`TextGenerator`** — Text generation with streaming and non-streaming methods. Every provider implements this.
- **`EmbeddingGenerator`** — Vector embeddings for semantic search and similarity.
- **`Transcriber`** — Audio-to-text transcription.
- **`ImageGenerator`** — Text-to-image generation.
- **`TokenCounter`** — Token counting, encoding, and decoding.
- **`AIProvider`** — Actor-based umbrella protocol with availability checks and cancellation.

### TextGenerator

The core protocol. All providers implement it.

```swift
public protocol TextGenerator: Sendable {
associatedtype ModelID: ModelIdentifying

func generate(_ prompt: String, model: ModelID, config: GenerateConfig) async throws -> String
func generate(messages: [Message], model: ModelID, config: GenerateConfig) async throws -> GenerationResult
func stream(_ prompt: String, model: ModelID, config: GenerateConfig) -> AsyncThrowingStream<String, Error>
func streamWithMetadata(messages: [Message], model: ModelID, config: GenerateConfig) -> AsyncThrowingStream<GenerationChunk, Error>
}
```

Write generic functions against `TextGenerator` to support any provider:

```swift
func summarize<P: TextGenerator>(with provider: P, model: P.ModelID, text: String) async throws -> String {
try await provider.generate("Summarize: \(text)", model: model, config: .default.maxTokens(200))
}
```

## Actor-Based Providers

Every provider is an actor, ensuring thread-safe concurrent use:

```swift
public actor AnthropicProvider: AIProvider, TextGenerator { ... }
public actor OpenAIProvider: AIProvider, TextGenerator, EmbeddingGenerator, TokenCounter, ImageGenerator { ... }
public actor MLXProvider: AIProvider, TextGenerator, TokenCounter { ... }
```

You can call providers from any task or actor without data races.

## Model Identifiers

Conduit uses type-safe model identifiers. Each provider defines its own `ModelID` type:

- `AnthropicModelID` — `.claudeOpus45`, `.claudeSonnet45`, `.claude35Sonnet`, `.claude3Haiku`
- `OpenAIModelID` — `.gpt4o`, `.gpt4oMini`, `.o1`, `.textEmbedding3Small`, `.dallE3`
- `ModelIdentifier` — `.mlx("repo/model")`, `.huggingFace("repo/model")`, `.foundationModels`

Provider-specific model IDs have static constants for common models:

```swift
// Anthropic
let response = try await anthropic.generate("Hello", model: .claudeSonnet45)

// OpenAI
let response = try await openai.generate("Hello", model: .gpt4o)

// MLX
let response = try await mlx.generate("Hello", model: .llama3_2_1b)

// HuggingFace — any model by repository name
let response = try await hf.generate("Hello", model: .huggingFace("meta-llama/Llama-3.1-70B-Instruct"))
```

## GenerateConfig

`GenerateConfig` controls generation behavior with a fluent API:

```swift
// Presets
let config = GenerateConfig.default // temperature: 0.7, topP: 0.9
let config = GenerateConfig.creative // temperature: 1.0, topP: 0.95
let config = GenerateConfig.precise // temperature: 0.3, topP: 0.8
let config = GenerateConfig.code // temperature: 0.2, topP: 0.9

// Fluent chaining
let config = GenerateConfig.default
.temperature(0.8)
.maxTokens(500)
.topP(0.9)
.stopSequences(["END"])
```

## Messages and MessageBuilder

The `Message` type represents conversation turns. Use the `Messages` result builder for ergonomic construction:

```swift
let messages = Messages {
Message.system("You are a Swift expert.")
Message.user("What are actors?")
}

let result = try await provider.generate(
messages: messages,
model: .claudeSonnet45,
config: .default
)
```

The builder supports conditionals and loops:

```swift
let messages = Messages {
Message.system("You are a helpful assistant.")

if includeContext {
Message.user("Context: \(context)")
}

for question in questions {
Message.user(question)
}
}
```

Multimodal content uses `ContentPart`:

```swift
let messages = Messages {
Message.user([
.text("What's in this image?"),
.image(base64Data: imageData, mimeType: "image/jpeg")
])
}
```

## Conditional Compilation Traits

Providers are gated behind Swift package traits. Guard new provider code with the appropriate flag:

```swift
#if CONDUIT_TRAIT_ANTHROPIC
let provider = AnthropicProvider(apiKey: "...")
#endif

#if CONDUIT_TRAIT_MLX
let provider = MLXProvider()
#endif
```

Some providers also use `#if canImport(...)` for platform detection:

```swift
#if canImport(FoundationModels)
let provider = FoundationModelsProvider()
#endif
```
Loading
Loading