Skip to content

Commit f3c06b1

Browse files
authored
feat(api): implement stateful responses api (mudler#122)
* feat(api): implement stateful responses api Signed-off-by: mudler <mudler@localai.io> * fix(tests): align client to API changes Signed-off-by: mudler <mudler@localai.io> --------- Signed-off-by: mudler <mudler@localai.io>
1 parent 86cb9f1 commit f3c06b1

6 files changed

Lines changed: 68 additions & 16 deletions

File tree

main.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ var localRAG = os.Getenv("LOCALAGENT_LOCALRAG_URL")
2121
var withLogs = os.Getenv("LOCALAGENT_ENABLE_CONVERSATIONS_LOGGING") == "true"
2222
var apiKeysEnv = os.Getenv("LOCALAGENT_API_KEYS")
2323
var imageModel = os.Getenv("LOCALAGENT_IMAGE_MODEL")
24+
var conversationDuration = os.Getenv("LOCALAGENT_CONVERSATION_DURATION")
2425

2526
func init() {
2627
if testModel == "" {
@@ -73,6 +74,7 @@ func main() {
7374
// Create the application
7475
app := webui.NewApp(
7576
webui.WithPool(pool),
77+
webui.WithConversationStoreduration(conversationDuration),
7678
webui.WithApiKeys(apiKeys...),
7779
webui.WithLLMAPIUrl(apiURL),
7880
webui.WithLLMAPIKey(apiKey),

pkg/client/agents.go

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,7 @@ func (c *Client) GetAgentConfig(name string) (*AgentConfig, error) {
5454

5555
// CreateAgent creates a new agent with the given configuration
5656
func (c *Client) CreateAgent(config *AgentConfig) error {
57-
resp, err := c.doRequest(http.MethodPost, "/create", config)
57+
resp, err := c.doRequest(http.MethodPost, "/api/agent/create", config)
5858
if err != nil {
5959
return err
6060
}
@@ -96,7 +96,7 @@ func (c *Client) UpdateAgentConfig(name string, config *AgentConfig) error {
9696

9797
// DeleteAgent removes an agent
9898
func (c *Client) DeleteAgent(name string) error {
99-
path := fmt.Sprintf("/delete/%s", name)
99+
path := fmt.Sprintf("/api/agent/%s", name)
100100
resp, err := c.doRequest(http.MethodDelete, path, nil)
101101
if err != nil {
102102
return err
@@ -116,7 +116,7 @@ func (c *Client) DeleteAgent(name string) error {
116116

117117
// PauseAgent pauses an agent
118118
func (c *Client) PauseAgent(name string) error {
119-
path := fmt.Sprintf("/pause/%s", name)
119+
path := fmt.Sprintf("/api/agent/pause/%s", name)
120120
resp, err := c.doRequest(http.MethodPut, path, nil)
121121
if err != nil {
122122
return err
@@ -136,7 +136,7 @@ func (c *Client) PauseAgent(name string) error {
136136

137137
// StartAgent starts a paused agent
138138
func (c *Client) StartAgent(name string) error {
139-
path := fmt.Sprintf("/start/%s", name)
139+
path := fmt.Sprintf("/api/agent/start/%s", name)
140140
resp, err := c.doRequest(http.MethodPut, path, nil)
141141
if err != nil {
142142
return err

services/connectors/conversationstracker.go

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -73,3 +73,12 @@ func (c *ConversationTracker[K]) AddMessage(key K, message openai.ChatCompletion
7373
c.currentconversation[key] = append(c.currentconversation[key], message)
7474
c.lastMessageTime[key] = time.Now()
7575
}
76+
77+
func (c *ConversationTracker[K]) SetConversation(key K, messages []openai.ChatCompletionMessage) {
78+
// Lock the conversation mutex to update the conversation history
79+
c.convMutex.Lock()
80+
defer c.convMutex.Unlock()
81+
82+
c.currentconversation[key] = messages
83+
c.lastMessageTime[key] = time.Now()
84+
}

webui/app.go

Lines changed: 22 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -10,11 +10,14 @@ import (
1010
"strings"
1111
"time"
1212

13+
"github.com/google/uuid"
1314
coreTypes "github.com/mudler/LocalAgent/core/types"
1415
"github.com/mudler/LocalAgent/pkg/llm"
1516
"github.com/mudler/LocalAgent/pkg/xlog"
1617
"github.com/mudler/LocalAgent/services"
18+
"github.com/mudler/LocalAgent/services/connectors"
1719
"github.com/mudler/LocalAgent/webui/types"
20+
"github.com/sashabaranov/go-openai"
1821
"github.com/sashabaranov/go-openai/jsonschema"
1922

2023
"github.com/mudler/LocalAgent/core/sse"
@@ -405,7 +408,7 @@ func (a *App) ListActions() func(c *fiber.Ctx) error {
405408
}
406409
}
407410

408-
func (a *App) Responses(pool *state.AgentPool) func(c *fiber.Ctx) error {
411+
func (a *App) Responses(pool *state.AgentPool, tracker *connectors.ConversationTracker[string]) func(c *fiber.Ctx) error {
409412
return func(c *fiber.Ctx) error {
410413
var request types.RequestBody
411414
if err := c.BodyParser(&request); err != nil {
@@ -414,9 +417,15 @@ func (a *App) Responses(pool *state.AgentPool) func(c *fiber.Ctx) error {
414417

415418
request.SetInputByType()
416419

417-
agentName := request.Model
420+
var previousResponseID string
421+
conv := []openai.ChatCompletionMessage{}
422+
if request.PreviousResponseID != nil {
423+
previousResponseID = *request.PreviousResponseID
424+
conv = tracker.GetConversation(previousResponseID)
425+
}
418426

419-
messages := request.ToChatCompletionMessages()
427+
agentName := request.Model
428+
messages := append(conv, request.ToChatCompletionMessages()...)
420429

421430
a := pool.GetAgent(agentName)
422431
if a == nil {
@@ -435,7 +444,17 @@ func (a *App) Responses(pool *state.AgentPool) func(c *fiber.Ctx) error {
435444
xlog.Info("we got a response from the agent", "agent", agentName, "response", res.Response)
436445
}
437446

447+
conv = append(conv, openai.ChatCompletionMessage{
448+
Role: "assistant",
449+
Content: res.Response,
450+
})
451+
452+
id := uuid.New().String()
453+
454+
tracker.SetConversation(id, conv)
455+
438456
response := types.ResponseBody{
457+
ID: id,
439458
Object: "response",
440459
// "created_at": 1741476542,
441460
CreatedAt: time.Now().Unix(),

webui/options.go

Lines changed: 23 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,20 @@
11
package webui
22

3-
import "github.com/mudler/LocalAgent/core/state"
3+
import (
4+
"time"
5+
6+
"github.com/mudler/LocalAgent/core/state"
7+
)
48

59
type Config struct {
6-
DefaultChunkSize int
7-
Pool *state.AgentPool
8-
ApiKeys []string
9-
LLMAPIURL string
10-
LLMAPIKey string
11-
LLMModel string
12-
StateDir string
10+
DefaultChunkSize int
11+
Pool *state.AgentPool
12+
ApiKeys []string
13+
LLMAPIURL string
14+
LLMAPIKey string
15+
LLMModel string
16+
StateDir string
17+
ConversationStoreDuration time.Duration
1318
}
1419

1520
type Option func(*Config)
@@ -20,6 +25,16 @@ func WithDefaultChunkSize(size int) Option {
2025
}
2126
}
2227

28+
func WithConversationStoreduration(duration string) Option {
29+
return func(c *Config) {
30+
d, err := time.ParseDuration(duration)
31+
if err != nil {
32+
d = 1 * time.Hour
33+
}
34+
c.ConversationStoreDuration = d
35+
}
36+
}
37+
2338
func WithStateDir(dir string) Option {
2439
return func(c *Config) {
2540
c.StateDir = dir

webui/routes.go

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,8 @@ import (
1313
"github.com/gofiber/fiber/v2/middleware/filesystem"
1414
"github.com/gofiber/fiber/v2/middleware/keyauth"
1515
"github.com/mudler/LocalAgent/core/sse"
16+
"github.com/mudler/LocalAgent/services/connectors"
17+
1618
"github.com/mudler/LocalAgent/core/state"
1719
"github.com/mudler/LocalAgent/core/types"
1820
"github.com/mudler/LocalAgent/pkg/xlog"
@@ -75,12 +77,17 @@ func (app *App) registerRoutes(pool *state.AgentPool, webapp *fiber.App) {
7577
webapp.Post("/api/chat/:name", app.Chat(pool))
7678
webapp.Post("/api/notify/:name", app.Notify(pool))
7779

78-
webapp.Post("/v1/responses", app.Responses(pool))
80+
conversationTracker := connectors.NewConversationTracker[string](app.config.ConversationStoreDuration)
81+
82+
webapp.Post("/v1/responses", app.Responses(pool, conversationTracker))
7983

8084
// New API endpoints for getting and updating agent configuration
8185
webapp.Get("/api/agent/:name/config", app.GetAgentConfig(pool))
8286
webapp.Put("/api/agent/:name/config", app.UpdateAgentConfig(pool))
8387

88+
// Metadata endpoint for agent configuration fields
89+
webapp.Get("/api/agent/config/metadata", app.GetAgentConfigMeta())
90+
8491
// Add endpoint for getting agent config metadata
8592
webapp.Get("/api/meta/agent/config", app.GetAgentConfigMeta())
8693

0 commit comments

Comments
 (0)