Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
f2de319
Add Foundation Models 27 request options
qoli Oct 9, 2026
cd1d6fc
Add session properties to dynamic instructions
qoli Oct 11, 2026
9481380
Add session properties to dynamic instructions
qoli Oct 11, 2026
a03fd6b
Add Foundation Models 27 dynamic profiles
qoli Oct 11, 2026
8cc3a74
Preserve dynamic profiles across tool continuations
qoli Oct 11, 2026
b9738ab
Document dynamic profiles
qoli Oct 11, 2026
da63029
Keep session property requests observable
qoli Oct 12, 2026
8b3a234
Keep session property requests observable
qoli Oct 12, 2026
9525809
Keep session property requests observable
qoli Oct 12, 2026
5fdf90e
Keep session property requests observable
qoli Oct 12, 2026
d0cae54
Isolate dynamic profile request state
qoli Oct 13, 2026
1ac8207
Isolate dynamic profile request state
qoli Oct 13, 2026
c350013
Fix dynamic profile continuation boundaries
qoli Oct 13, 2026
4879808
Isolate dynamic profile request state
qoli Oct 13, 2026
51c5bcc
Fix dynamic profile continuation boundaries
qoli Oct 13, 2026
d1c8413
Merge remote-tracking branch 'upstream/main' into codex/upstream-dyna…
qoli Oct 14, 2026
49f681f
Merge updated dynamic profiles into tool continuations
qoli Oct 14, 2026
375ebd7
Merge branch 'codex/upstream-profile-tool-continuations' into codex/u…
qoli Oct 14, 2026
93e4d4d
Fix session property observation and typed defaults
qoli Oct 15, 2026
e99341e
Merge branch 'codex/upstream-session-properties' into codex/upstream-…
qoli Oct 15, 2026
cdeed51
Fix dynamic profile callback concurrency
qoli Oct 15, 2026
8e218df
Merge reviewed profile fixes into tool continuations
qoli Oct 15, 2026
9a27552
Merge branch 'codex/upstream-profile-tool-continuations' into codex/u…
qoli Oct 15, 2026
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
91 changes: 91 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -515,6 +515,86 @@ but never become part of the session's transcript.
> Otherwise, it throws `SystemLanguageModel.Error.dynamicInstructionsUnavailable`
> for a session with dynamic instructions.

### Foundation Models 27 request options

`ContextOptions` mirrors Foundation Models 27's schema-in-prompt and reasoning-level
settings for compatibility-layer request construction. `GenerationOptions` also
provides the OS 27 `samplingMode` spelling and `ToolCallingMode` values:

```swift
let context = ContextOptions(
includeSchemaInPrompt: true,
reasoningLevel: .moderate
)
let options = GenerationOptions(
samplingMode: .greedy,
toolCallingMode: .required
)
```

The older `sampling` property and initializer remain source-compatible.
Tool-calling mode is preserved in Codable transcripts and forwarded by the
Foundation Models bridge on OS 27. Other model providers can read the same
option from their request's `GenerationOptions`.

### Dynamic Profiles

`LanguageModelSession.DynamicProfile` mirrors the Foundation Models 27 profile
API on supported AnyLanguageModel deployment targets. A profile chooses one
active `Profile` for each actual model request and can change its model,
instructions, tools, generation options, context options, and transcript
projection without rebuilding the session:

```swift
extension SessionPropertyValues {
@SessionPropertyEntry var isReviewing = false
}

struct EditingProfile: LanguageModelSession.DynamicProfile {
let model: any LanguageModel
@SessionProperty(\.isReviewing) private var isReviewing

var body: some LanguageModelSession.DynamicProfile {
if isReviewing {
Profile {
Instructions("Review the current draft.")
ReviewTool()
}
.model(model)
.temperature(0.1)
} else {
Profile {
Instructions("Help edit the current draft.")
EditTool()
}
.model(model)
.temperature(0.7)
}
}
}

let session = LanguageModelSession(
profile: EditingProfile(model: model),
history: savedHistory
)
session.properties.isReviewing = true
```

Value modifiers use call-site, inner-profile, then outer-profile precedence.
Lifecycle callbacks accumulate. `historyTransform` changes only the transcript
projection for that request; `session.transcript` remains the canonical history.
`@SessionProperty` exposes isolated session state to profiles, dynamic
instructions, and tools. The built-in `history` property is writable from a
profile or lifecycle callback and read-only while dynamic instructions or a Tool
is active.

External compatibility models that own Tool continuation use
`resolvedRequestContext(including:options:)` before every provider request. The
`including:` entries are the canonical Tool calls and outputs completed in the
current response but not yet committed to the session transcript. Calls execute
against the tools in the producing request context; the next resolved context is
used only for the continuation request.

### Reasoning in the transcript

Reasoning is transcript content, separate from the answer in `response.content`.
Expand Down Expand Up @@ -654,6 +734,17 @@ say which API they follow.
- `DynamicInstructions`, its builder, and `LanguageModelSession.init(model:dynamicInstructions:history:)`:
[dynamic instructions](#dynamic-instructions),
which follow the Foundation Models 27 API.
- `ContextOptions`, `GenerationOptions.samplingMode`, and
`GenerationOptions.ToolCallingMode`:
[request options](#foundation-models-27-request-options), which follow the
Foundation Models 27 API while remaining available on the package's existing
deployment targets.
- `LanguageModelSession.DynamicProfile`, profile modifiers, lifecycle callbacks,
session properties, and `LanguageModelSession.init(profile:history:)`:
[dynamic profiles](#dynamic-profiles), which mirror Foundation Models 27 on
earlier deployment targets. `resolvedRequestContext(including:options:)` and
the explicit compatibility lifecycle hooks are AnyLanguageModel extensions for
external model implementations until the package adopts the executor contract.
- `Transcript.Entry.reasoning` and `Transcript.Reasoning`:
[reasoning in the transcript](#reasoning-in-the-transcript),
which follows the Foundation Models 27 API.
Expand Down
27 changes: 27 additions & 0 deletions Sources/AnyLanguageModel/ContextOptions.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
/// Options that configure a model's request context.
///
/// This mirrors the Foundation Models 27 request-context surface while keeping
/// the package available on its existing deployment targets.
public struct ContextOptions: Sendable, Equatable {
/// The amount of reasoning the model should perform for a request.
public enum ReasoningLevel: Sendable, Equatable {
case light
case moderate
case deep
case custom(String)
}

/// Whether a structured-generation schema should also appear in the prompt.
public var includeSchemaInPrompt: Bool?

/// The amount of reasoning the model should perform.
public var reasoningLevel: ReasoningLevel?

public init(
includeSchemaInPrompt: Bool? = nil,
reasoningLevel: ReasoningLevel? = nil
) {
self.includeSchemaInPrompt = includeSchemaInPrompt
self.reasoningLevel = reasoningLevel
}
}
2 changes: 1 addition & 1 deletion Sources/AnyLanguageModel/DynamicInstructions.swift
Original file line number Diff line number Diff line change
Expand Up @@ -337,7 +337,7 @@ extension Instructions: PrimitiveDynamicInstructions {
}
}

private func resolveDynamicInstructions(
func resolveDynamicInstructions(
_ dynamicInstructions: any DynamicInstructions
) -> ResolvedDynamicInstructions {
func resolve<Content>(_ content: Content) -> ResolvedDynamicInstructions
Expand Down
Loading