Enha: agent client redo [WIP]
This commit is contained in:
@@ -4,11 +4,12 @@ package agent
|
|||||||
// ones who do their own tools calls
|
// ones who do their own tools calls
|
||||||
// ones that works only with the output
|
// ones that works only with the output
|
||||||
|
|
||||||
// A: main chat -> agent (handles everything: tool + processing)
|
// A: main chat -> agent (handles everything: tool + processing), supports tool chaining
|
||||||
// B: main chat -> tool -> agent (process tool output)
|
// B: main chat -> tool -> agent (process tool output)
|
||||||
|
|
||||||
// AgenterA gets a task "find out weather in london"
|
// AgenterA gets a task like "go to the webpage, login and take a screenshot (tell me what you see)"
|
||||||
// proceeds to make tool calls on its own
|
// proceeds to make a plan and executes it.
|
||||||
|
// returns with final result or an error
|
||||||
type AgenterA interface {
|
type AgenterA interface {
|
||||||
ProcessTask(task string) []byte
|
ProcessTask(task string) []byte
|
||||||
}
|
}
|
||||||
|
|||||||
40
agent/pw_agent.go
Normal file
40
agent/pw_agent.go
Normal file
@@ -0,0 +1,40 @@
|
|||||||
|
package agent
|
||||||
|
|
||||||
|
// PWAgent: is AgenterA type agent (enclosed with tool chaining)
|
||||||
|
// sysprompt explain tools and how to plan for execution
|
||||||
|
type PWAgent struct {
|
||||||
|
*AgentClient
|
||||||
|
sysprompt string
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewWebAgentB creates a WebAgentB that uses the given formatting function
|
||||||
|
func NewPWAgent(client *AgentClient, sysprompt string) *PWAgent {
|
||||||
|
return &PWAgent{AgentClient: client, sysprompt: sysprompt}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *PWAgent) ProcessTask(task string) []byte {
|
||||||
|
req, err := a.FormFirstMsg(a.sysprompt, task)
|
||||||
|
if err != nil {
|
||||||
|
a.Log().Error("PWAgent failed to process the request", "error", err)
|
||||||
|
return []byte("PWAgent failed to process the request; err: " + err.Error())
|
||||||
|
}
|
||||||
|
toolCallLimit := 10
|
||||||
|
for i := 0; i < toolCallLimit; i++ {
|
||||||
|
resp, err := a.LLMRequest(req)
|
||||||
|
if err != nil {
|
||||||
|
a.Log().Error("failed to process the request", "error", err)
|
||||||
|
return []byte("failed to process the request; err: " + err.Error())
|
||||||
|
}
|
||||||
|
toolCall, hasToolCall := findToolCall(resp)
|
||||||
|
if !hasToolCall {
|
||||||
|
return resp
|
||||||
|
}
|
||||||
|
// check resp for tool calls
|
||||||
|
// make tool call
|
||||||
|
// add tool call resp to body
|
||||||
|
// send new request too lmm
|
||||||
|
tooResp := toolCall(resp)
|
||||||
|
req, err = a.FormMsg(toolResp)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
@@ -33,6 +33,10 @@ type AgentClient struct {
|
|||||||
cfg *config.Config
|
cfg *config.Config
|
||||||
getToken func() string
|
getToken func() string
|
||||||
log *slog.Logger
|
log *slog.Logger
|
||||||
|
chatBody *models.ChatBody
|
||||||
|
sysprompt string
|
||||||
|
lastToolCallID string
|
||||||
|
tools []models.Tool
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewAgentClient(cfg *config.Config, log *slog.Logger, gt func() string) *AgentClient {
|
func NewAgentClient(cfg *config.Config, log *slog.Logger, gt func() string) *AgentClient {
|
||||||
@@ -47,8 +51,29 @@ func (ag *AgentClient) Log() *slog.Logger {
|
|||||||
return ag.log
|
return ag.log
|
||||||
}
|
}
|
||||||
|
|
||||||
func (ag *AgentClient) FormMsg(sysprompt, msg string) (io.Reader, error) {
|
func (ag *AgentClient) FormFirstMsg(sysprompt, msg string) (io.Reader, error) {
|
||||||
b, err := ag.buildRequest(sysprompt, msg)
|
ag.sysprompt = sysprompt
|
||||||
|
ag.chatBody = &models.ChatBody{
|
||||||
|
Messages: []models.RoleMsg{
|
||||||
|
{Role: "system", Content: ag.sysprompt},
|
||||||
|
{Role: "user", Content: msg},
|
||||||
|
},
|
||||||
|
Stream: false,
|
||||||
|
Model: ag.cfg.CurrentModel,
|
||||||
|
}
|
||||||
|
b, err := ag.buildRequest()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return bytes.NewReader(b), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ag *AgentClient) FormMsg(msg string) (io.Reader, error) {
|
||||||
|
m := models.RoleMsg{
|
||||||
|
Role: "tool", Content: msg,
|
||||||
|
}
|
||||||
|
ag.chatBody.Messages = append(ag.chatBody.Messages, m)
|
||||||
|
b, err := ag.buildRequest()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@@ -56,75 +81,52 @@ func (ag *AgentClient) FormMsg(sysprompt, msg string) (io.Reader, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// buildRequest creates the appropriate LLM request based on the current API endpoint.
|
// buildRequest creates the appropriate LLM request based on the current API endpoint.
|
||||||
func (ag *AgentClient) buildRequest(sysprompt, msg string) ([]byte, error) {
|
func (ag *AgentClient) buildRequest() ([]byte, error) {
|
||||||
api := ag.cfg.CurrentAPI
|
isCompletion, isChat, isDeepSeek, isOpenRouter := detectAPI(ag.cfg.CurrentAPI)
|
||||||
model := ag.cfg.CurrentModel
|
ag.log.Debug("agent building request", "api", ag.cfg.CurrentAPI, "isCompletion", isCompletion, "isChat", isChat, "isDeepSeek", isDeepSeek, "isOpenRouter", isOpenRouter)
|
||||||
messages := []models.RoleMsg{
|
|
||||||
{Role: "system", Content: sysprompt},
|
|
||||||
{Role: "user", Content: msg},
|
|
||||||
}
|
|
||||||
// Determine API type
|
|
||||||
isCompletion, isChat, isDeepSeek, isOpenRouter := detectAPI(api)
|
|
||||||
ag.log.Debug("agent building request", "api", api, "isCompletion", isCompletion, "isChat", isChat, "isDeepSeek", isDeepSeek, "isOpenRouter", isOpenRouter)
|
|
||||||
// Build prompt for completion endpoints
|
// Build prompt for completion endpoints
|
||||||
if isCompletion {
|
if isCompletion {
|
||||||
var sb strings.Builder
|
var sb strings.Builder
|
||||||
for i := range messages {
|
for i := range ag.chatBody.Messages {
|
||||||
sb.WriteString(messages[i].ToPrompt())
|
sb.WriteString(ag.chatBody.Messages[i].ToPrompt())
|
||||||
sb.WriteString("\n")
|
sb.WriteString("\n")
|
||||||
}
|
}
|
||||||
prompt := strings.TrimSpace(sb.String())
|
prompt := strings.TrimSpace(sb.String())
|
||||||
switch {
|
switch {
|
||||||
case isDeepSeek:
|
case isDeepSeek:
|
||||||
// DeepSeek completion
|
// DeepSeek completion
|
||||||
req := models.NewDSCompletionReq(prompt, model, defaultProps["temperature"], []string{})
|
req := models.NewDSCompletionReq(prompt, ag.chatBody.Model, defaultProps["temperature"], []string{})
|
||||||
req.Stream = false // Agents don't need streaming
|
req.Stream = false // Agents don't need streaming
|
||||||
return json.Marshal(req)
|
return json.Marshal(req)
|
||||||
case isOpenRouter:
|
case isOpenRouter:
|
||||||
// OpenRouter completion
|
// OpenRouter completion
|
||||||
req := models.NewOpenRouterCompletionReq(model, prompt, defaultProps, []string{})
|
req := models.NewOpenRouterCompletionReq(ag.chatBody.Model, prompt, defaultProps, []string{})
|
||||||
req.Stream = false // Agents don't need streaming
|
req.Stream = false // Agents don't need streaming
|
||||||
return json.Marshal(req)
|
return json.Marshal(req)
|
||||||
default:
|
default:
|
||||||
// Assume llama.cpp completion
|
// Assume llama.cpp completion
|
||||||
req := models.NewLCPReq(prompt, model, nil, defaultProps, []string{})
|
req := models.NewLCPReq(prompt, ag.chatBody.Model, nil, defaultProps, []string{})
|
||||||
req.Stream = false // Agents don't need streaming
|
req.Stream = false // Agents don't need streaming
|
||||||
return json.Marshal(req)
|
return json.Marshal(req)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Chat completions endpoints
|
|
||||||
if isChat || !isCompletion {
|
|
||||||
chatBody := &models.ChatBody{
|
|
||||||
Model: model,
|
|
||||||
Stream: false, // Agents don't need streaming
|
|
||||||
Messages: messages,
|
|
||||||
}
|
|
||||||
switch {
|
switch {
|
||||||
case isDeepSeek:
|
case isDeepSeek:
|
||||||
// DeepSeek chat
|
// DeepSeek chat
|
||||||
req := models.NewDSChatReq(*chatBody)
|
req := models.NewDSChatReq(*ag.chatBody)
|
||||||
return json.Marshal(req)
|
return json.Marshal(req)
|
||||||
case isOpenRouter:
|
case isOpenRouter:
|
||||||
// OpenRouter chat - agents don't use reasoning by default
|
// OpenRouter chat - agents don't use reasoning by default
|
||||||
req := models.NewOpenRouterChatReq(*chatBody, defaultProps, "")
|
req := models.NewOpenRouterChatReq(*ag.chatBody, defaultProps, ag.cfg.ReasoningEffort)
|
||||||
return json.Marshal(req)
|
return json.Marshal(req)
|
||||||
default:
|
default:
|
||||||
// Assume llama.cpp chat (OpenAI format)
|
// Assume llama.cpp chat (OpenAI format)
|
||||||
req := models.OpenAIReq{
|
req := models.OpenAIReq{
|
||||||
ChatBody: chatBody,
|
ChatBody: ag.chatBody,
|
||||||
Tools: nil,
|
Tools: ag.tools,
|
||||||
}
|
}
|
||||||
return json.Marshal(req)
|
return json.Marshal(req)
|
||||||
}
|
}
|
||||||
}
|
|
||||||
// Fallback (should not reach here)
|
|
||||||
ag.log.Warn("unknown API, using default chat completions format", "api", api)
|
|
||||||
chatBody := &models.ChatBody{
|
|
||||||
Model: model,
|
|
||||||
Stream: false, // Agents don't need streaming
|
|
||||||
Messages: messages,
|
|
||||||
}
|
|
||||||
return json.Marshal(chatBody)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (ag *AgentClient) LLMRequest(body io.Reader) ([]byte, error) {
|
func (ag *AgentClient) LLMRequest(body io.Reader) ([]byte, error) {
|
||||||
|
|||||||
Reference in New Issue
Block a user