Add weighted random workflow steps and a random choice agent tool
This commit is contained in:
@@ -9,7 +9,7 @@ typealias TeamMember = Components.Schemas.TeamMember
|
||||
@Observable
|
||||
final class StepNode: Identifiable {
|
||||
enum Kind {
|
||||
case stage, order, parallel
|
||||
case stage, order, parallel, random
|
||||
}
|
||||
|
||||
let id = UUID()
|
||||
@@ -17,6 +17,7 @@ final class StepNode: Identifiable {
|
||||
var name = ""
|
||||
var model = ""
|
||||
var prompt = ""
|
||||
var weight = 0.0
|
||||
var agents: [TeamMember]?
|
||||
var children: [StepNode] = []
|
||||
weak var parent: StepNode?
|
||||
@@ -35,6 +36,8 @@ final class StepNode: Identifiable {
|
||||
self.init(kind: .order, children: order.map { StepNode($0) })
|
||||
} else if let parallel = step.parallel {
|
||||
self.init(kind: .parallel, children: parallel.map { StepNode($0) })
|
||||
} else if let random = step.random {
|
||||
self.init(kind: .random, children: random.map { StepNode($0) })
|
||||
} else {
|
||||
self.init(kind: .stage)
|
||||
name = step.stage ?? ""
|
||||
@@ -42,19 +45,27 @@ final class StepNode: Identifiable {
|
||||
prompt = step.prompt ?? ""
|
||||
agents = step.agents
|
||||
}
|
||||
weight = step.weight ?? 0
|
||||
}
|
||||
|
||||
var step: WorkflowStep {
|
||||
switch kind {
|
||||
let w = parent?.kind == .random ? weight : nil
|
||||
return switch kind {
|
||||
case .stage:
|
||||
WorkflowStep(stage: name, model: agents == nil ? model : nil, prompt: prompt, agents: agents)
|
||||
WorkflowStep(stage: name, model: agents == nil ? model : nil, prompt: prompt, weight: w, agents: agents)
|
||||
case .order:
|
||||
WorkflowStep(order: children.map(\.step))
|
||||
WorkflowStep(order: children.map(\.step), weight: w)
|
||||
case .parallel:
|
||||
WorkflowStep(parallel: children.map(\.step))
|
||||
WorkflowStep(parallel: children.map(\.step), weight: w)
|
||||
case .random:
|
||||
WorkflowStep(random: children.map(\.step), weight: w)
|
||||
}
|
||||
}
|
||||
|
||||
var odds: Double {
|
||||
parent?.kind == .random ? weight : 1
|
||||
}
|
||||
|
||||
var index: Int {
|
||||
parent!.children.firstIndex { $0 === self }!
|
||||
}
|
||||
@@ -132,21 +143,27 @@ func stageDiagram(_ stages: [GraphStage]) -> String {
|
||||
return lines.joined(separator: "\n")
|
||||
}
|
||||
|
||||
private func percent(_ odds: Double) -> String {
|
||||
(odds * 100).formatted(.number.precision(.fractionLength(0)).grouping(.never)) + "%"
|
||||
}
|
||||
|
||||
@MainActor
|
||||
private func stepDiagram(_ root: StepNode) -> String {
|
||||
var stages: [GraphStage] = []
|
||||
func walk(_ node: StepNode, _ before: [Int]) -> [Int] {
|
||||
func walk(_ node: StepNode, _ before: [Int], _ odds: Double) -> [Int] {
|
||||
let odds = odds * node.odds
|
||||
switch node.kind {
|
||||
case .order:
|
||||
return node.children.reduce(before) { walk($1, $0) }
|
||||
case .parallel:
|
||||
return node.children.flatMap { walk($0, before) }
|
||||
return node.children.reduce(before) { walk($1, $0, odds) }
|
||||
case .parallel, .random:
|
||||
return node.children.flatMap { walk($0, before, odds) }
|
||||
case .stage:
|
||||
stages.append(GraphStage(name: node.name, agents: node.agents, after: before))
|
||||
let name = odds < 1 ? "\(node.name) · \(percent(odds))" : node.name
|
||||
stages.append(GraphStage(name: name, agents: node.agents, after: before))
|
||||
return [stages.count - 1]
|
||||
}
|
||||
}
|
||||
_ = walk(root, [])
|
||||
_ = walk(root, [], 1)
|
||||
return stageDiagram(stages)
|
||||
}
|
||||
|
||||
@@ -318,6 +335,9 @@ private struct StepRow: View {
|
||||
VStack(alignment: .leading, spacing: 6) {
|
||||
HStack(alignment: .firstTextBaseline, spacing: 6) {
|
||||
Text(guide).foregroundStyle(Palette.muted)
|
||||
if node.parent?.kind == .random {
|
||||
WeightField(node: node)
|
||||
}
|
||||
switch node.kind {
|
||||
case .stage:
|
||||
Button {
|
||||
@@ -343,6 +363,9 @@ private struct StepRow: View {
|
||||
case .parallel:
|
||||
Text("in parallel").foregroundStyle(Palette.muted)
|
||||
Spacer()
|
||||
case .random:
|
||||
Text("one at random").foregroundStyle(Palette.muted)
|
||||
Spacer()
|
||||
}
|
||||
actions
|
||||
}
|
||||
@@ -386,11 +409,12 @@ private struct StepRow: View {
|
||||
Button("Add stage after") { addAfter() }
|
||||
Button("Run in parallel with a new stage") { wrap(.parallel) }
|
||||
Button("Group with a new stage in order") { wrap(.order) }
|
||||
case .order, .parallel:
|
||||
Button("Pick at random between it and a new stage") { wrap(.random) }
|
||||
case .order, .parallel, .random:
|
||||
Button("Add stage inside") { node.insert(stage(), at: node.children.count) }
|
||||
Button("Add stage after") { addAfter() }
|
||||
Button(node.kind == .order ? "Change to in parallel" : "Change to in order") {
|
||||
node.kind = node.kind == .order ? .parallel : .order
|
||||
ForEach(groupKinds.filter { $0.0 != node.kind }, id: \.1) { kind, label in
|
||||
Button("Change to \(label)") { node.kind = kind }
|
||||
}
|
||||
Button("Ungroup") { ungroup() }
|
||||
}
|
||||
@@ -416,6 +440,8 @@ private struct StepRow: View {
|
||||
.fixedSize()
|
||||
}
|
||||
|
||||
private let groupKinds: [(StepNode.Kind, String)] = [(.order, "in order"), (.parallel, "in parallel"), (.random, "one at random")]
|
||||
|
||||
private func stage() -> StepNode {
|
||||
newStage(root, store.defaultModel)
|
||||
}
|
||||
@@ -429,8 +455,14 @@ private struct StepRow: View {
|
||||
let i = node.index
|
||||
node.detach()
|
||||
let group = StepNode(kind: kind)
|
||||
let added = stage()
|
||||
group.weight = node.weight
|
||||
if kind == .random {
|
||||
node.weight = 0.5
|
||||
added.weight = 0.5
|
||||
}
|
||||
group.insert(node, at: 0)
|
||||
group.insert(stage(), at: 1)
|
||||
group.insert(added, at: 1)
|
||||
parent.insert(group, at: i)
|
||||
}
|
||||
|
||||
@@ -477,6 +509,23 @@ private struct StepRow: View {
|
||||
}
|
||||
}
|
||||
|
||||
private struct WeightField: View {
|
||||
@Bindable var node: StepNode
|
||||
|
||||
var body: some View {
|
||||
HStack(spacing: 4) {
|
||||
TextField("weight", value: $node.weight, format: .number)
|
||||
.textFieldStyle(.plain)
|
||||
.multilineTextAlignment(.trailing)
|
||||
.frame(width: 36)
|
||||
.padding(.horizontal, 4)
|
||||
.background(Palette.raised, in: RoundedRectangle(cornerRadius: 4))
|
||||
.overlay(RoundedRectangle(cornerRadius: 4).stroke(Palette.border))
|
||||
}
|
||||
.help("Weight: its chance of being picked, from 0 to 1. The weights in one random add up to 1.")
|
||||
}
|
||||
}
|
||||
|
||||
struct ModelPicker: View {
|
||||
@Binding var model: String
|
||||
|
||||
|
||||
Reference in New Issue
Block a user