forked from intitni/CopilotForXcode
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathAgent.swift
More file actions
165 lines (139 loc) · 4.64 KB
/
Agent.swift
File metadata and controls
165 lines (139 loc) · 4.64 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
import Foundation
public struct AgentAction: Equatable {
public var toolName: String
public var toolInput: String
public var log: String
public var observation: String?
public init(toolName: String, toolInput: String, log: String, observation: String? = nil) {
self.toolName = toolName
self.toolInput = toolInput
self.log = log
self.observation = observation
}
public func observationAvailable(_ observation: String) -> AgentAction {
var newAction = self
newAction.observation = observation
return newAction
}
}
public extension CallbackEvents {
struct AgentDidFinish: CallbackEvent {
public let info: AgentFinish
}
var agentDidFinish: AgentDidFinish.Type {
AgentDidFinish.self
}
struct AgentActionDidStart: CallbackEvent {
public let info: AgentAction
}
var agentActionDidStart: AgentActionDidStart.Type {
AgentActionDidStart.self
}
struct AgentActionDidEnd: CallbackEvent {
public let info: AgentAction
}
var agentActionDidEnd: AgentActionDidEnd.Type {
AgentActionDidEnd.self
}
}
public struct AgentFinish: Equatable {
public var returnValue: String
public var log: String
public init(returnValue: String, log: String) {
self.returnValue = returnValue
self.log = log
}
}
public enum AgentNextStep: Equatable {
case actions([AgentAction])
case finish(AgentFinish)
}
public enum AgentScratchPad: Equatable {
case text(String)
case messages([String])
var isEmpty: Bool {
switch self {
case let .text(text):
return text.isEmpty
case let .messages(messages):
return messages.isEmpty
}
}
}
public struct AgentInput<T> {
var input: T
var thoughts: AgentScratchPad
public init(input: T, thoughts: AgentScratchPad) {
self.input = input
self.thoughts = thoughts
}
}
extension AgentInput: Equatable where T: Equatable {}
public enum AgentEarlyStopHandleType: Equatable {
case force
case generate
}
public protocol Agent {
associatedtype Input
var chatModelChain: ChatModelChain<AgentInput<Input>> { get }
var observationPrefix: String { get }
var llmPrefix: String { get }
func validateTools(tools: [AgentTool]) throws
func constructScratchpad(intermediateSteps: [AgentAction]) -> AgentScratchPad
func parseOutput(_ output: String) -> AgentNextStep
}
public extension Agent {
func getFullInputs(input: Input, intermediateSteps: [AgentAction]) -> AgentInput<Input> {
let thoughts = constructScratchpad(intermediateSteps: intermediateSteps)
return AgentInput(input: input, thoughts: thoughts)
}
func plan(
input: Input,
intermediateSteps: [AgentAction],
callbackManagers: [CallbackManager]
) async throws -> AgentNextStep {
let input = getFullInputs(input: input, intermediateSteps: intermediateSteps)
let output = try await chatModelChain.call(input, callbackManagers: callbackManagers)
return parseOutput(output.content ?? "")
}
func returnStoppedResponse(
input: Input,
earlyStoppedHandleType: AgentEarlyStopHandleType,
intermediateSteps: [AgentAction],
callbackManagers: [CallbackManager]
) async throws -> AgentFinish {
switch earlyStoppedHandleType {
case .force:
return AgentFinish(
returnValue: "Agent stopped due to iteration limit or time limit.",
log: ""
)
case .generate:
var thoughts = constructBaseScratchpad(intermediateSteps: intermediateSteps)
thoughts += """
\(llmPrefix)I now need to return a final answer based on the previous steps:
(Please continue with `Final Answer:`)
"""
let input = AgentInput(input: input, thoughts: .text(thoughts))
let output = try await chatModelChain.call(input, callbackManagers: callbackManagers)
let reply = output.content ?? ""
let nextAction = parseOutput(reply)
switch nextAction {
case let .finish(finish):
return finish
case .actions:
return AgentFinish(returnValue: reply, log: reply)
}
}
}
func constructBaseScratchpad(intermediateSteps: [AgentAction]) -> String {
var thoughts = ""
for step in intermediateSteps {
thoughts += """
\(step.log)
\(observationPrefix)\(step.observation ?? "")
"""
}
return thoughts
}
}