Compare commits
4 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
f289e824df | ||
|
|
9e52438877 | ||
|
|
c4618896cf | ||
|
|
ee1667d51a |
2
Makefile
2
Makefile
@@ -9,7 +9,7 @@ cleanup-tests:
|
||||
docker compose down
|
||||
|
||||
tests: prepare-tests
|
||||
LOCALAGI_MODEL="arcee-agent" LOCALAI_API_URL="http://localhost:8081" LOCALAGI_API_URL="http://localhost:8080" $(GOCMD) run github.com/onsi/ginkgo/v2/ginkgo --fail-fast -v -r ./...
|
||||
LOCALAGI_MODEL="gemma-3-12b-it-qat" LOCALAI_API_URL="http://localhost:8081" LOCALAGI_API_URL="http://localhost:8080" $(GOCMD) run github.com/onsi/ginkgo/v2/ginkgo --fail-fast -v -r ./...
|
||||
|
||||
run-nokb:
|
||||
$(MAKE) run KBDISABLEINDEX=true
|
||||
|
||||
@@ -114,7 +114,7 @@ LocalAGI supports multiple hardware configurations through Docker Compose profil
|
||||
- Supports text, multimodal, and image generation models
|
||||
- Run with: `docker compose -f docker-compose.nvidia.yaml up`
|
||||
- Default models:
|
||||
- Text: `arcee-agent`
|
||||
- Text: `gemma-3-12b-it-qat`
|
||||
- Multimodal: `minicpm-v-2_6`
|
||||
- Image: `sd-1.5-ggml`
|
||||
- Environment variables:
|
||||
@@ -130,7 +130,7 @@ LocalAGI supports multiple hardware configurations through Docker Compose profil
|
||||
- Supports text, multimodal, and image generation models
|
||||
- Run with: `docker compose -f docker-compose.intel.yaml up`
|
||||
- Default models:
|
||||
- Text: `arcee-agent`
|
||||
- Text: `gemma-3-12b-it-qat`
|
||||
- Multimodal: `minicpm-v-2_6`
|
||||
- Image: `sd-1.5-ggml`
|
||||
- Environment variables:
|
||||
@@ -161,7 +161,7 @@ docker compose -f docker-compose.intel.yaml up
|
||||
```
|
||||
|
||||
If no models are specified, it will use the defaults:
|
||||
- Text model: `arcee-agent`
|
||||
- Text model: `gemma-3-12b-it-qat`
|
||||
- Multimodal model: `minicpm-v-2_6`
|
||||
- Image model: `sd-1.5-ggml`
|
||||
|
||||
|
||||
@@ -226,7 +226,10 @@ var _ = Describe("Agent test", func() {
|
||||
WithLLMAPIKey(apiKeyURL),
|
||||
WithTimeout("10m"),
|
||||
WithActions(
|
||||
actions.NewSearch(map[string]string{}),
|
||||
&TestAction{response: map[string]string{
|
||||
"boston": testActionResult,
|
||||
"milan": testActionResult2,
|
||||
}},
|
||||
),
|
||||
EnablePlanning,
|
||||
EnableForceReasoning,
|
||||
@@ -238,18 +241,21 @@ var _ = Describe("Agent test", func() {
|
||||
defer agent.Stop()
|
||||
|
||||
result := agent.Ask(
|
||||
types.WithText("Thoroughly plan a trip to San Francisco from Venice, Italy; check flight times, visa requirements and whether electrical items are allowed in cabin luggage."),
|
||||
types.WithText("Use the plan tool to do two actions in sequence: search for the weather in boston and search for the weather in milan"),
|
||||
)
|
||||
Expect(len(result.State)).To(BeNumerically(">", 1))
|
||||
|
||||
actionsExecuted := []string{}
|
||||
actionResults := []string{}
|
||||
for _, r := range result.State {
|
||||
xlog.Info(r.Result)
|
||||
actionsExecuted = append(actionsExecuted, r.Action.Definition().Name.String())
|
||||
actionResults = append(actionResults, r.ActionResult.Result)
|
||||
}
|
||||
Expect(actionsExecuted).To(ContainElement("search_internet"), fmt.Sprint(result))
|
||||
Expect(actionsExecuted).To(ContainElement("get_weather"), fmt.Sprint(result))
|
||||
Expect(actionsExecuted).To(ContainElement("plan"), fmt.Sprint(result))
|
||||
|
||||
Expect(actionResults).To(ContainElement(testActionResult), fmt.Sprint(result))
|
||||
Expect(actionResults).To(ContainElement(testActionResult2), fmt.Sprint(result))
|
||||
})
|
||||
|
||||
It("Can initiate conversations", func() {
|
||||
|
||||
@@ -36,7 +36,8 @@ func NewSSEObserver(agent string, manager sse.Manager) *SSEObserver {
|
||||
}
|
||||
|
||||
func (s *SSEObserver) NewObservable() *types.Observable {
|
||||
id := atomic.AddInt32(&s.maxID, 1)
|
||||
id := atomic.AddInt32(&s.maxID, 1)
|
||||
|
||||
return &types.Observable{
|
||||
ID: id - 1,
|
||||
Agent: s.agent,
|
||||
|
||||
@@ -166,7 +166,56 @@ func (a *AgentPool) CreateAgent(name string, agentConfig *AgentConfig) error {
|
||||
}
|
||||
}(a.pool[name])
|
||||
|
||||
return a.startAgentWithConfig(name, agentConfig)
|
||||
return a.startAgentWithConfig(name, agentConfig, nil)
|
||||
}
|
||||
|
||||
func (a *AgentPool) RecreateAgent(name string, agentConfig *AgentConfig) error {
|
||||
a.Lock()
|
||||
defer a.Unlock()
|
||||
|
||||
oldAgent := a.agents[name]
|
||||
var o *types.Observable
|
||||
obs := oldAgent.Observer()
|
||||
if obs != nil {
|
||||
o = obs.NewObservable()
|
||||
o.Name = "Restarting Agent"
|
||||
o.Icon = "sync"
|
||||
o.Creation = &types.Creation{}
|
||||
obs.Update(*o)
|
||||
}
|
||||
|
||||
stateFile, characterFile := a.stateFiles(name)
|
||||
|
||||
os.Remove(stateFile)
|
||||
os.Remove(characterFile)
|
||||
|
||||
oldAgent.Stop()
|
||||
|
||||
a.pool[name] = *agentConfig
|
||||
delete(a.agents, name)
|
||||
|
||||
if err := a.save(); err != nil {
|
||||
if obs != nil {
|
||||
o.Completion = &types.Completion{Error: err.Error()}
|
||||
obs.Update(*o)
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
if err := a.startAgentWithConfig(name, agentConfig, obs); err != nil {
|
||||
if obs != nil {
|
||||
o.Completion = &types.Completion{Error: err.Error()}
|
||||
obs.Update(*o)
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
if obs != nil {
|
||||
o.Completion = &types.Completion{}
|
||||
obs.Update(*o)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func createAgentAvatar(APIURL, APIKey, model, imageModel, avatarDir string, agent AgentConfig) error {
|
||||
@@ -268,8 +317,13 @@ func (a *AgentPool) GetStatusHistory(name string) *Status {
|
||||
return a.agentStatus[name]
|
||||
}
|
||||
|
||||
func (a *AgentPool) startAgentWithConfig(name string, config *AgentConfig) error {
|
||||
manager := sse.NewManager(5)
|
||||
func (a *AgentPool) startAgentWithConfig(name string, config *AgentConfig, obs Observer) error {
|
||||
var manager sse.Manager
|
||||
if m, ok := a.managers[name]; ok {
|
||||
manager = m
|
||||
} else {
|
||||
manager = sse.NewManager(5)
|
||||
}
|
||||
ctx := context.Background()
|
||||
model := a.defaultModel
|
||||
multimodalModel := a.defaultMultimodalModel
|
||||
@@ -331,6 +385,10 @@ func (a *AgentPool) startAgentWithConfig(name string, config *AgentConfig) error
|
||||
// dynamicPrompts = append(dynamicPrompts, p.ToMap())
|
||||
// }
|
||||
|
||||
if obs == nil {
|
||||
obs = NewSSEObserver(name, manager)
|
||||
}
|
||||
|
||||
opts := []Option{
|
||||
WithModel(model),
|
||||
WithLLMAPIURL(a.apiURL),
|
||||
@@ -407,7 +465,7 @@ func (a *AgentPool) startAgentWithConfig(name string, config *AgentConfig) error
|
||||
c.AgentResultCallback()(state)
|
||||
}
|
||||
}),
|
||||
WithObserver(NewSSEObserver(name, manager)),
|
||||
WithObserver(obs),
|
||||
}
|
||||
|
||||
if config.HUD {
|
||||
@@ -510,7 +568,7 @@ func (a *AgentPool) StartAll() error {
|
||||
if a.agents[name] != nil { // Agent already started
|
||||
continue
|
||||
}
|
||||
if err := a.startAgentWithConfig(name, &config); err != nil {
|
||||
if err := a.startAgentWithConfig(name, &config, nil); err != nil {
|
||||
xlog.Error("Failed to start agent", "name", name, "error", err)
|
||||
}
|
||||
}
|
||||
@@ -548,7 +606,7 @@ func (a *AgentPool) Start(name string) error {
|
||||
return nil
|
||||
}
|
||||
if config, ok := a.pool[name]; ok {
|
||||
return a.startAgentWithConfig(name, &config)
|
||||
return a.startAgentWithConfig(name, &config, nil)
|
||||
}
|
||||
|
||||
return fmt.Errorf("agent %s not found", name)
|
||||
|
||||
@@ -7,7 +7,7 @@ services:
|
||||
# Image list (dockerhub): https://hub.docker.com/r/localai/localai
|
||||
image: localai/localai:master-ffmpeg-core
|
||||
command:
|
||||
- ${MODEL_NAME:-arcee-agent}
|
||||
- ${MODEL_NAME:-gemma-3-12b-it-qat}
|
||||
- ${MULTIMODAL_MODEL:-minicpm-v-2_6}
|
||||
- ${IMAGE_MODEL:-sd-1.5-ggml}
|
||||
- granite-embedding-107m-multilingual
|
||||
@@ -59,7 +59,7 @@ services:
|
||||
- 8080:3000
|
||||
#image: quay.io/mudler/localagi:master
|
||||
environment:
|
||||
- LOCALAGI_MODEL=${MODEL_NAME:-arcee-agent}
|
||||
- LOCALAGI_MODEL=${MODEL_NAME:-gemma-3-12b-it-qat}
|
||||
- LOCALAGI_MULTIMODAL_MODEL=${MULTIMODAL_MODEL:-minicpm-v-2_6}
|
||||
- LOCALAGI_IMAGE_MODEL=${IMAGE_MODEL:-sd-1.5-ggml}
|
||||
- LOCALAGI_LLM_API_URL=http://localai:8080
|
||||
|
||||
@@ -33,6 +33,8 @@ type StateDescription struct {
|
||||
CurrentURL string `json:"current_url"`
|
||||
PageTitle string `json:"page_title"`
|
||||
PageContentDescription string `json:"page_content_description"`
|
||||
Screenshot string `json:"screenshot"`
|
||||
ScreenshotMimeType string `json:"screenshot_mime_type"` // MIME type of the screenshot (e.g., "image/png")
|
||||
}
|
||||
|
||||
// StateHistory represents the complete history of states during agent execution
|
||||
|
||||
@@ -61,7 +61,8 @@ func (b *BrowserAgentRunner) Run(ctx context.Context, params types.ActionParams)
|
||||
historyStr += fmt.Sprintf(" Description: %s\n\n", stateHistory.States[len(stateHistory.States)-1].PageContentDescription)
|
||||
|
||||
return types.ActionResult{
|
||||
Result: fmt.Sprintf("Browser agent completed successfully. History:\n%s", historyStr),
|
||||
Result: fmt.Sprintf("Browser agent completed successfully. History:\n%s", historyStr),
|
||||
Metadata: map[string]interface{}{"browser_agent_history": stateHistory},
|
||||
}, nil
|
||||
}
|
||||
|
||||
|
||||
@@ -77,8 +77,9 @@ func (i *IRC) Start(a *agent.Agent) {
|
||||
}
|
||||
i.conn.UseTLS = false
|
||||
i.conn.AddCallback("001", func(e *irc.Event) {
|
||||
xlog.Info("Connected to IRC server", "server", i.server)
|
||||
xlog.Info("Connected to IRC server", "server", i.server, "arguments", e.Arguments)
|
||||
i.conn.Join(i.channel)
|
||||
i.nickname = e.Arguments[0]
|
||||
xlog.Info("Joined channel", "channel", i.channel)
|
||||
})
|
||||
|
||||
@@ -207,6 +208,13 @@ func (i *IRC) Start(a *agent.Agent) {
|
||||
|
||||
// Start the IRC client in a goroutine
|
||||
go i.conn.Loop()
|
||||
go func() {
|
||||
select {
|
||||
case <-a.Context().Done():
|
||||
i.conn.Quit()
|
||||
return
|
||||
}
|
||||
}()
|
||||
}
|
||||
|
||||
// IRCConfigMeta returns the metadata for IRC connector configuration fields
|
||||
|
||||
12
webui/app.go
12
webui/app.go
@@ -176,17 +176,7 @@ func (a *App) UpdateAgentConfig(pool *state.AgentPool) func(c *fiber.Ctx) error
|
||||
return errorJSONMessage(c, err.Error())
|
||||
}
|
||||
|
||||
// Remove the agent first
|
||||
if err := pool.Remove(agentName); err != nil {
|
||||
return errorJSONMessage(c, "Error removing agent: "+err.Error())
|
||||
}
|
||||
|
||||
// Create agent with new config
|
||||
if err := pool.CreateAgent(agentName, &newConfig); err != nil {
|
||||
// Try to restore the old configuration if update fails
|
||||
if restoreErr := pool.CreateAgent(agentName, oldConfig); restoreErr != nil {
|
||||
return errorJSONMessage(c, fmt.Sprintf("Failed to update agent and restore failed: %v, %v", err, restoreErr))
|
||||
}
|
||||
if err := pool.RecreateAgent(agentName, &newConfig); err != nil {
|
||||
return errorJSONMessage(c, "Error updating agent: "+err.Error())
|
||||
}
|
||||
|
||||
|
||||
@@ -18,7 +18,7 @@
|
||||
"eslint-plugin-react-refresh": "^0.4.19",
|
||||
"globals": "^16.0.0",
|
||||
"react-router-dom": "^7.5.1",
|
||||
"vite": "^6.3.1",
|
||||
"vite": "^6.3.2",
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -427,7 +427,7 @@
|
||||
|
||||
"uri-js": ["uri-js@4.4.1", "", { "dependencies": { "punycode": "^2.1.0" } }, "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg=="],
|
||||
|
||||
"vite": ["vite@6.3.1", "", { "dependencies": { "esbuild": "^0.25.0", "fdir": "^6.4.3", "picomatch": "^4.0.2", "postcss": "^8.5.3", "rollup": "^4.34.9", "tinyglobby": "^0.2.12" }, "optionalDependencies": { "fsevents": "~2.3.3" }, "peerDependencies": { "@types/node": "^18.0.0 || ^20.0.0 || >=22.0.0", "jiti": ">=1.21.0", "less": "*", "lightningcss": "^1.21.0", "sass": "*", "sass-embedded": "*", "stylus": "*", "sugarss": "*", "terser": "^5.16.0", "tsx": "^4.8.1", "yaml": "^2.4.2" }, "optionalPeers": ["@types/node", "jiti", "less", "lightningcss", "sass", "sass-embedded", "stylus", "sugarss", "terser", "tsx", "yaml"], "bin": { "vite": "bin/vite.js" } }, "sha512-kkzzkqtMESYklo96HKKPE5KKLkC1amlsqt+RjFMlX2AvbRB/0wghap19NdBxxwGZ+h/C6DLCrcEphPIItlGrRQ=="],
|
||||
"vite": ["vite@6.3.2", "", { "dependencies": { "esbuild": "^0.25.0", "fdir": "^6.4.3", "picomatch": "^4.0.2", "postcss": "^8.5.3", "rollup": "^4.34.9", "tinyglobby": "^0.2.12" }, "optionalDependencies": { "fsevents": "~2.3.3" }, "peerDependencies": { "@types/node": "^18.0.0 || ^20.0.0 || >=22.0.0", "jiti": ">=1.21.0", "less": "*", "lightningcss": "^1.21.0", "sass": "*", "sass-embedded": "*", "stylus": "*", "sugarss": "*", "terser": "^5.16.0", "tsx": "^4.8.1", "yaml": "^2.4.2" }, "optionalPeers": ["@types/node", "jiti", "less", "lightningcss", "sass", "sass-embedded", "stylus", "sugarss", "terser", "tsx", "yaml"], "bin": { "vite": "bin/vite.js" } }, "sha512-ZSvGOXKGceizRQIZSz7TGJ0pS3QLlVY/9hwxVh17W3re67je1RKYzFHivZ/t0tubU78Vkyb9WnHPENSBCzbckg=="],
|
||||
|
||||
"which": ["which@2.0.2", "", { "dependencies": { "isexe": "^2.0.0" }, "bin": { "node-which": "./bin/node-which" } }, "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA=="],
|
||||
|
||||
|
||||
@@ -24,6 +24,6 @@
|
||||
"eslint-plugin-react-refresh": "^0.4.19",
|
||||
"globals": "^16.0.0",
|
||||
"react-router-dom": "^7.5.1",
|
||||
"vite": "^6.3.1"
|
||||
"vite": "^6.3.2"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -99,8 +99,17 @@ function AgentStatus() {
|
||||
creation: data.creation,
|
||||
progress: data.progress,
|
||||
completion: data.completion,
|
||||
// children are always built client-side
|
||||
};
|
||||
// Events can be received out of order
|
||||
if (data.creation)
|
||||
updated.creation = data.creation;
|
||||
if (data.completion)
|
||||
updated.completion = data.completion;
|
||||
if (data.parent_id && !prevMap[data.parent_id])
|
||||
prevMap[data.parent_id] = {
|
||||
id: data.parent_id,
|
||||
name: "unknown",
|
||||
};
|
||||
const newMap = { ...prevMap, [data.id]: updated };
|
||||
setObservableTree(buildObservableTree(newMap));
|
||||
return newMap;
|
||||
|
||||
Reference in New Issue
Block a user