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
Original file line number Diff line number Diff line change
Expand Up @@ -99,4 +99,98 @@ extension AgentDemoViewModel {
lastError = error.localizedDescription
}
}

func runStreamedStructuredOutputDemo() async {
guard session != nil else {
lastError = "Sign in before running the streamed structured output demo."
return
}
guard !isRunningStructuredStreamingDemo else {
return
}

isRunningStructuredStreamingDemo = true
lastError = nil
structuredStreamingResult = nil
structuredStreamingError = nil
defer {
isRunningStructuredStreamingDemo = false
}

do {
let thread = try await runtime.createThread(
title: "Structured Output: Streamed Delivery Update",
personaStack: Self.supportPersona
)
let request = DemoStructuredOutputExamples.streamedStructuredRequest()
if showResolvedInstructionsDebug {
lastResolvedInstructions = try await runtime.resolvedInstructionsPreview(
for: thread.id,
request: request
)
lastResolvedInstructionsThreadTitle = thread.title ?? "Structured Output: Streamed Delivery Update"
}

let stream = try await runtime.streamMessage(
request,
in: thread.id,
expecting: StreamedStructuredDeliveryUpdate.self
)

var visibleText = ""
var partialSnapshots: [StreamedStructuredDeliveryUpdate] = []
var committedPayload: StreamedStructuredDeliveryUpdate?

for try await event in stream {
switch event {
case let .assistantMessageDelta(_, _, delta):
visibleText += delta

case let .messageCommitted(message):
if message.role == .assistant {
visibleText = message.displayText
}

case let .structuredOutputPartial(partial):
if partialSnapshots.last != partial {
partialSnapshots.append(partial)
}

case let .structuredOutputCommitted(payload):
committedPayload = payload

case let .turnFailed(error):
throw error

default:
break
}
}

let messages = await runtime.messages(for: thread.id)
let persistedMetadata = messages.last(where: { $0.role == .assistant })?.structuredOutput

guard let committedPayload else {
throw AgentRuntimeError.structuredOutputMissing(
formatName: StreamedStructuredDeliveryUpdate.responseFormat.name
)
}

structuredStreamingResult = StructuredStreamingDemoResult(
threadID: thread.id,
threadTitle: thread.title ?? "Structured Output: Streamed Delivery Update",
prompt: DemoStructuredOutputExamples.streamedStructuredPrompt,
visibleText: visibleText.trimmingCharacters(in: .whitespacesAndNewlines),
partialSnapshots: partialSnapshots,
committedPayload: committedPayload,
persistedMetadata: persistedMetadata
)
threads = await runtime.threads()
activeThreadID = thread.id
setMessages(messages)
} catch {
structuredStreamingError = error.localizedDescription
lastError = error.localizedDescription
}
}
}
13 changes: 13 additions & 0 deletions DemoApp/AssistantRuntimeDemoApp/Shared/AgentDemoViewModel.swift
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,16 @@ struct StructuredOutputDemoImportResult: Sendable {
let summary: StructuredImportedContentSummary
}

struct StructuredStreamingDemoResult: Sendable {
let threadID: String
let threadTitle: String
let prompt: String
let visibleText: String
let partialSnapshots: [StreamedStructuredDeliveryUpdate]
let committedPayload: StreamedStructuredDeliveryUpdate
let persistedMetadata: AgentStructuredOutputMetadata?
}

struct GuidedMemoryDemoResult: Sendable {
let record: MemoryRecord
let diagnostics: MemoryStoreDiagnostics
Expand Down Expand Up @@ -135,8 +145,11 @@ final class AgentDemoViewModel: @unchecked Sendable {
var pendingComposerImages: [AgentImageAttachment] = []
var composerText = ""
var isRunningStructuredOutputDemo = false
var isRunningStructuredStreamingDemo = false
var structuredShippingReplyResult: StructuredOutputDemoDraftResult?
var structuredImportedSummaryResult: StructuredOutputDemoImportResult?
var structuredStreamingResult: StructuredStreamingDemoResult?
var structuredStreamingError: String?
var isRunningMemoryDemo = false
var automaticMemoryResult: AutomaticMemoryDemoResult?
var automaticPolicyMemoryResult: AutomaticPolicyMemoryDemoResult?
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,26 @@ struct StructuredImportedContentSummary: AgentStructuredOutput, Sendable {
)
}

struct StreamedStructuredDeliveryUpdate: AgentStructuredOutput, Sendable, Hashable {
let statusHeadline: String
let customerPromise: String
let nextAction: String

static let responseFormat = AgentStructuredOutputFormat(
name: "streamed_delivery_update",
description: "A structured operational delivery update produced alongside visible assistant narration.",
schema: .object(
properties: [
"statusHeadline": .string(),
"customerPromise": .string(),
"nextAction": .string(),
],
required: ["statusHeadline", "customerPromise", "nextAction"],
additionalProperties: false
)
)
}

enum DemoStructuredOutputExamples {
static let shippingCustomerMessage = """
My package was supposed to arrive yesterday for a birthday on Saturday. Tracking has not moved in two days and I need to know whether it will make it in time.
Expand All @@ -57,6 +77,9 @@ enum DemoStructuredOutputExamples {
"""

static let importedArticleURL = URL(string: "https://github.com/timazed/CodexKit")!
static let streamedStructuredPrompt = """
The package is delayed ahead of a birthday delivery. Talk to the customer like an in-app support assistant while you work through the situation. Stream a short human-readable response only. Do not restate the final structured delivery fields in prose because the app receives those separately. Then provide the final typed delivery update for the app.
"""

static func shippingReplyRequest() -> UserMessageRequest {
UserMessageRequest(
Expand All @@ -78,4 +101,8 @@ enum DemoStructuredOutputExamples {
)
)
}

static func streamedStructuredRequest() -> UserMessageRequest {
UserMessageRequest(text: streamedStructuredPrompt)
}
}
Loading
Loading