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
46 changes: 19 additions & 27 deletions CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,16 +13,12 @@ Kimi, MiniMax, HuggingFace, MLX, CoreML, llama.cpp, and Apple Foundation Models)
- Platforms: iOS 17+, macOS 14+, visionOS 1+, Linux (cloud providers only)
- License: MIT

The package is deliberately split into two products so end users can opt into a
minimal surface or the full implementation:

- `Conduit` — the **facade** product (`Sources/ConduitFacade/ConduitFacade.swift`).
Exposes the small, agent-friendly API: `Conduit`, `Provider`, `Session`,
`Model`, `RunOptions`, `ToolSetBuilder`.
- `ConduitAdvanced` — the full implementation surface
(`Sources/Conduit/` — note: the directory named `Conduit` maps to the
`ConduitAdvanced` target in `Package.swift`). Use when callers need direct
provider actors, protocols, and low-level controls.
The package exposes one public product:

- `Conduit` — the canonical public API and full implementation surface
(`Sources/Conduit/`). It exposes the small, agent-friendly facade types
(`Conduit`, `Provider`, `Session`, `Model`, `RunOptions`, `ToolSetBuilder`)
alongside direct provider actors, protocols, and low-level controls.
- `ConduitMacros` — the Swift macro compiler plugin (`@Generable`, `@Guide`).

## Repository Layout
Expand All @@ -31,7 +27,7 @@ minimal surface or the full implementation:
.
├── Package.swift # SPM manifest, traits, targets
├── Sources/
│ ├── Conduit/ # → target: ConduitAdvanced (full surface)
│ ├── Conduit/ # → target: Conduit (full surface)
│ │ ├── Conduit.swift # version + re-export markers
│ │ ├── ConduitAPI.swift # facade-style `Conduit`/`Provider`/`Session`
│ │ ├── ChatSession.swift # @Observable stateful multi-turn session
Expand Down Expand Up @@ -62,8 +58,6 @@ minimal surface or the full implementation:
│ │ ├── Services/ # HFMetadataService, VLMDetector, MLX checks
│ │ ├── Utilities/ # JsonRepair, PartialJSONDecoder, SSEParser, GlobMatcher
│ │ └── Documentation.docc/ # DocC catalog
│ ├── ConduitFacade/
│ │ └── ConduitFacade.swift # → target: Conduit (thin facade re-exports)
│ └── ConduitMacros/
│ ├── ConduitMacrosPlugin.swift
│ ├── GenerableMacro.swift # @Generable expansion
Expand All @@ -87,10 +81,9 @@ minimal surface or the full implementation:
└── README.md
```

> Important path gotcha: `Package.swift` places the `ConduitAdvanced` target at
> `path: "Sources/Conduit"`, and the `Conduit` target at `path: "Sources/ConduitFacade"`.
> **The directory name does not match the target name.** When adding files, make sure
> they land under the correct directory for the target you mean to modify.
> Important path note: `Package.swift` places the canonical `Conduit` target at
> `path: "Sources/Conduit"`. New public API and implementation files should land
> under that directory unless they belong to the macro target.

## Build, Test, Lint

Expand Down Expand Up @@ -191,7 +184,7 @@ Note for doc/example updates (from `AGENTS.md`): current API names are
`GeneratedContent`, `GenerationSchema`, `Tool`, `Transcript`. **Do not**
reintroduce legacy terms like `StructuredContent`, `Schema`, or `AITool`.

### Facade API (`Sources/Conduit/ConduitAPI.swift` + `Sources/ConduitFacade/ConduitFacade.swift`)
### Facade API (`Sources/Conduit/ConduitAPI.swift`)
Canonical surface for agents and human callers:

```swift
Expand Down Expand Up @@ -236,9 +229,9 @@ cancellation via `cancel()`, and an opt-in `WarmupConfig` (`.default` or
2. **Traits gate providers.** Any new provider code must live behind its
`CONDUIT_TRAIT_*` flag (and `canImport(...)` where needed). Do not import
provider-specific SDKs from trait-agnostic files.
3. **Module layout.** Do not move files between `Sources/Conduit/` (the
`ConduitAdvanced` target) and `Sources/ConduitFacade/` (the `Conduit`
target) without understanding the facade/advanced split.
3. **Module layout.** `Sources/Conduit/` is the canonical public target. Keep
public API and implementation in that target unless a file belongs to
`ConduitMacros`.
4. **Linux must keep building.** Cloud providers, utilities, and macros must
compile on Linux. MLX, CoreML, and Foundation Models are Apple-only.
`Tests/ConduitTests/LinuxCompatibilityTests.swift` guards this.
Expand Down Expand Up @@ -266,13 +259,12 @@ cancellation via `cancel()`, and an opt-in `WarmupConfig` (`.default` or
2. Mark the actor `public actor <Name>Provider: AIProvider, TextGenerator`
(add other capability protocols as applicable).
3. Add a `.trait(...)` in `Package.swift` and a matching
`.define("CONDUIT_TRAIT_<NAME>", .when(traits: ["<Name>"]))` in all three
affected targets (`ConduitAdvanced`, `Conduit`, `ConduitTests`, and
`ConduitMLXTests` if relevant).
`.define("CONDUIT_TRAIT_<NAME>", .when(traits: ["<Name>"]))` in the affected
targets (`Conduit`, `ConduitTests`, and `ConduitMLXTests` if relevant).
4. Wrap all provider code in `#if CONDUIT_TRAIT_<NAME>` and, for native SDKs,
also in `#if canImport(<SDK>)`.
5. Add a facade factory in `ConduitAPI.swift` (and re-export in
`ConduitFacade.swift` if it needs to be visible from the minimal module).
5. Add a convenience factory in `ConduitAPI.swift` when the provider should be
available through the facade-style API.
6. Add unit tests in `Tests/ConduitTests/Providers/<Name>/` and update the
Linux CI trait list in `.github/workflows/linux.yml` if the provider should
be covered there.
Expand All @@ -281,7 +273,7 @@ cancellation via `cancel()`, and an opt-in `WarmupConfig` (`.default` or

## Common Tasks

- **Update front-facing API** → edit `ConduitAPI.swift` + `ConduitFacade.swift`,
- **Update front-facing API** → edit `ConduitAPI.swift`,
then refresh `front-facing-api.md`, the `docs/guide/` pages, and the DocC
catalog so examples still compile under `DocumentationExamplesTests`.
- **Change provider defaults** → update the provider's `*Configuration.swift`
Expand Down
2 changes: 1 addition & 1 deletion Package.resolved

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

49 changes: 20 additions & 29 deletions Package.swift
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ if includeMLXImageDependencies {
)
}

var conduitAdvancedDependencies: [Target.Dependency] = [
var conduitDependencies: [Target.Dependency] = [
"ConduitMacros",
.product(name: "OrderedCollections", package: "swift-collections"),
.product(name: "Numerics", package: "swift-numerics"),
Expand All @@ -60,7 +60,7 @@ var conduitAdvancedDependencies: [Target.Dependency] = [
]

if includeMLXDependencies {
conduitAdvancedDependencies.append(
conduitDependencies.append(
contentsOf: [
.product(name: "MLX", package: "mlx-swift", condition: .when(traits: ["MLX"])),
.product(name: "MLXLMCommon", package: "mlx-swift-lm", condition: .when(traits: ["MLX"])),
Expand All @@ -73,20 +73,18 @@ if includeMLXDependencies {
}

if includeMLXImageDependencies {
conduitAdvancedDependencies.append(
conduitDependencies.append(
.product(name: "StableDiffusion", package: "mlx-swift-examples", condition: .when(traits: ["MLX"]))
)
}

var conduitTestDependencies: [Target.Dependency] = [
"Conduit",
"ConduitAdvanced",
.product(name: "Numerics", package: "swift-numerics"),
]

var conduitMLXTestDependencies: [Target.Dependency] = [
"Conduit",
"ConduitAdvanced",
]

if includeMLXDependencies {
Expand Down Expand Up @@ -135,10 +133,6 @@ let package = Package(
name: "Conduit",
targets: ["Conduit"]
),
.library(
name: "ConduitAdvanced",
targets: ["ConduitAdvanced"]
),
],
traits: [
.trait(
Expand All @@ -161,6 +155,14 @@ let package = Package(
name: "MiniMax",
description: "Enable MiniMax provider support (OpenAI-compatible)"
),
.trait(
name: "Gemini",
description: "Enable Google Gemini native API provider support"
),
.trait(
name: "Ollama",
description: "Enable native Ollama local runtime management support"
),
.trait(
name: "MLX",
description: "Enable MLX on-device inference (Apple Silicon only)"
Expand Down Expand Up @@ -194,33 +196,18 @@ let package = Package(
.unsafeFlags(["-module-cache-path", swiftModuleCachePath])
]
),
.target(
name: "ConduitAdvanced",
dependencies: conduitAdvancedDependencies,
path: "Sources/Conduit",
swiftSettings: [
.define("CONDUIT_TRAIT_OPENAI", .when(traits: ["OpenAI"])),
.define("CONDUIT_TRAIT_OPENROUTER", .when(traits: ["OpenRouter"])),
.define("CONDUIT_TRAIT_ANTHROPIC", .when(traits: ["Anthropic"])),
.define("CONDUIT_TRAIT_KIMI", .when(traits: ["Kimi"])),
.define("CONDUIT_TRAIT_MINIMAX", .when(traits: ["MiniMax"])),
.define("CONDUIT_TRAIT_MLX", .when(traits: ["MLX"])),
.define("CONDUIT_TRAIT_COREML", .when(traits: ["CoreML"])),
.enableExperimentalFeature("StrictConcurrency")
]
),
.target(
name: "Conduit",
dependencies: [
"ConduitAdvanced"
],
path: "Sources/ConduitFacade",
dependencies: conduitDependencies,
path: "Sources/Conduit",
swiftSettings: [
.define("CONDUIT_TRAIT_OPENAI", .when(traits: ["OpenAI"])),
.define("CONDUIT_TRAIT_OPENROUTER", .when(traits: ["OpenRouter"])),
.define("CONDUIT_TRAIT_ANTHROPIC", .when(traits: ["Anthropic"])),
.define("CONDUIT_TRAIT_KIMI", .when(traits: ["Kimi"])),
.define("CONDUIT_TRAIT_MINIMAX", .when(traits: ["MiniMax"])),
.define("CONDUIT_TRAIT_GEMINI", .when(traits: ["Gemini"])),
.define("CONDUIT_TRAIT_OLLAMA", .when(traits: ["Ollama"])),
.define("CONDUIT_TRAIT_MLX", .when(traits: ["MLX"])),
.define("CONDUIT_TRAIT_COREML", .when(traits: ["CoreML"])),
.enableExperimentalFeature("StrictConcurrency")
Expand All @@ -236,6 +223,8 @@ let package = Package(
.define("CONDUIT_TRAIT_ANTHROPIC", .when(traits: ["Anthropic"])),
.define("CONDUIT_TRAIT_KIMI", .when(traits: ["Kimi"])),
.define("CONDUIT_TRAIT_MINIMAX", .when(traits: ["MiniMax"])),
.define("CONDUIT_TRAIT_GEMINI", .when(traits: ["Gemini"])),
.define("CONDUIT_TRAIT_OLLAMA", .when(traits: ["Ollama"])),
.define("CONDUIT_TRAIT_MLX", .when(traits: ["MLX"])),
.define("CONDUIT_TRAIT_COREML", .when(traits: ["CoreML"])),
.unsafeFlags(["-module-cache-path", swiftModuleCachePath]),
Expand All @@ -253,6 +242,8 @@ let package = Package(
.define("CONDUIT_TRAIT_ANTHROPIC", .when(traits: ["Anthropic"])),
.define("CONDUIT_TRAIT_KIMI", .when(traits: ["Kimi"])),
.define("CONDUIT_TRAIT_MINIMAX", .when(traits: ["MiniMax"])),
.define("CONDUIT_TRAIT_GEMINI", .when(traits: ["Gemini"])),
.define("CONDUIT_TRAIT_OLLAMA", .when(traits: ["Ollama"])),
.define("CONDUIT_TRAIT_MLX", .when(traits: ["MLX"])),
.define("CONDUIT_TRAIT_COREML", .when(traits: ["CoreML"])),
.unsafeFlags(["-module-cache-path", swiftModuleCachePath]),
Expand All @@ -262,7 +253,7 @@ let package = Package(
.testTarget(
name: "ConduitMacrosTests",
dependencies: [
"ConduitAdvanced",
"Conduit",
"ConduitMacros",
.product(name: "SwiftSyntaxMacrosTestSupport", package: "swift-syntax"),
],
Expand Down
Loading
Loading