From ca6d075a2271f61ec8eb89a287f20d8dda89f661 Mon Sep 17 00:00:00 2001 From: Max Date: Thu, 27 Feb 2025 16:32:25 +0800 Subject: [PATCH] Add shared space methods to JavaScript bridge - Implement Set, Get, Del, and Clear methods for shared space interactions - Add error handling and validation for shared space method calls - Enhance context management with Release method to clear shared space - Update context initialization to create a default memory shared space --- neo/api.go | 1 + neo/assistant/object.go | 139 ++++++++++++++++++++++++++++++++++++++++ neo/context/context.go | 17 ++++- 3 files changed, 154 insertions(+), 3 deletions(-) diff --git a/neo/api.go b/neo/api.go index d4d34913..71e866da 100644 --- a/neo/api.go +++ b/neo/api.go @@ -230,6 +230,7 @@ func (neo *DSL) handleChat(c *gin.Context) { // Set the context with validated chat_id ctx, cancel := chatctx.NewWithCancel(sid, chatID, c.Query("context")) defer cancel() + defer ctx.Release() // Release the context after the request is done neo.Answer(ctx, content, c) } diff --git a/neo/assistant/object.go b/neo/assistant/object.go index 3ca75ec9..42971e91 100644 --- a/neo/assistant/object.go +++ b/neo/assistant/object.go @@ -52,6 +52,145 @@ func (ast *Assistant) InitObject(v8ctx *v8.Context, c *gin.Context, context chat v8ctx.WithFunction("Send", jsSend) v8ctx.WithFunction("Call", jsCall) v8ctx.WithFunction("Assets", jsAssets) + + // Shared space methods + v8ctx.WithFunction("Set", jsSet) + v8ctx.WithFunction("Get", jsGet) + v8ctx.WithFunction("Del", jsDel) + v8ctx.WithFunction("Clear", jsClear) +} + +// jsSet function, set a value to the shared space +func jsSet(info *v8go.FunctionCallbackInfo) *v8go.Value { + global, err := global(info) + if err != nil { + return bridge.JsException(info.Context(), err.Error()) + } + + if global.ChatContext.SharedSpace == nil { + return bridge.JsException(info.Context(), "Shared space is not set") + } + + args := info.Args() + if len(args) < 2 { + return bridge.JsException(info.Context(), "Set requires at least two arguments") + } + + if !args[0].IsString() { + return bridge.JsException(info.Context(), "Set requires a valid key") + } + + // Validate the key + key := args[0].String() + if key == "" { + return bridge.JsException(info.Context(), "Set requires a valid key") + } + + // Validate the value + value, err := bridge.GoValue(args[1], info.Context()) + if err != nil { + return bridge.JsException(info.Context(), err.Error()) + } + + // Set the value + err = global.ChatContext.SharedSpace.Set(key, value) + if err != nil { + return bridge.JsException(info.Context(), err.Error()) + } + + return nil +} + +// jsGet function, get a value from the shared space +func jsGet(info *v8go.FunctionCallbackInfo) *v8go.Value { + global, err := global(info) + if err != nil { + return bridge.JsException(info.Context(), err.Error()) + } + + if global.ChatContext.SharedSpace == nil { + return bridge.JsException(info.Context(), "Shared space is not set") + } + + args := info.Args() + if len(args) < 1 { + return bridge.JsException(info.Context(), "Get requires at least one argument") + } + + if !args[0].IsString() { + return bridge.JsException(info.Context(), "Get requires a valid key") + } + + // Get the key + key := args[0].String() + if key == "" { + return bridge.JsException(info.Context(), "Get requires a valid key") + } + + // Get the value + value, err := global.ChatContext.SharedSpace.Get(key) + if err != nil { + return bridge.JsException(info.Context(), err.Error()) + } + + jsValue, err := bridge.JsValue(info.Context(), value) + if err != nil { + return bridge.JsException(info.Context(), err.Error()) + } + + return jsValue +} + +// jsDel function, delete a value from the shared space +func jsDel(info *v8go.FunctionCallbackInfo) *v8go.Value { + global, err := global(info) + if err != nil { + return bridge.JsException(info.Context(), err.Error()) + } + + if global.ChatContext.SharedSpace == nil { + return bridge.JsException(info.Context(), "Shared space is not set") + } + + args := info.Args() + if len(args) < 1 { + return bridge.JsException(info.Context(), "Get requires at least one argument") + } + + if !args[0].IsString() { + return bridge.JsException(info.Context(), "Get requires a valid key") + } + + // Get the key + key := args[0].String() + if key == "" { + return bridge.JsException(info.Context(), "Get requires a valid key") + } + + err = global.ChatContext.SharedSpace.Delete(key) + if err != nil { + return bridge.JsException(info.Context(), err.Error()) + } + + return nil +} + +func jsClear(info *v8go.FunctionCallbackInfo) *v8go.Value { + global, err := global(info) + if err != nil { + return bridge.JsException(info.Context(), err.Error()) + } + + if global.ChatContext.SharedSpace == nil { + return bridge.JsException(info.Context(), "Shared space is not set") + } + + err = global.ChatContext.SharedSpace.Clear() + if err != nil { + return bridge.JsException(info.Context(), err.Error()) + } + + return nil } // jsAssets function, get the assets content diff --git a/neo/context/context.go b/neo/context/context.go index 4962c3a0..26cc5450 100644 --- a/neo/context/context.go +++ b/neo/context/context.go @@ -5,6 +5,7 @@ import ( "time" jsoniter "github.com/json-iterator/go" + "github.com/yaoapp/gou/plan" "github.com/yaoapp/kun/log" ) @@ -14,8 +15,8 @@ type Context struct { Sid string `json:"sid" yaml:"-"` // Session ID ChatID string `json:"chat_id,omitempty"` // Chat ID, use to select chat AssistantID string `json:"assistant_id,omitempty"` // Assistant ID, use to select assistant - Stack string `json:"stack,omitempty"` - Path string `json:"pathname,omitempty"` + Stack string `json:"stack,omitempty"` // will be removed in the future + Path string `json:"pathname,omitempty"` // wiil be rename to path FormData map[string]interface{} `json:"formdata,omitempty"` Field *Field `json:"field,omitempty"` Namespace string `json:"namespace,omitempty"` @@ -26,6 +27,7 @@ type Context struct { Upload *FileUpload `json:"upload,omitempty"` Version bool `json:"version,omitempty"` // Version support RAG bool `json:"rag,omitempty"` // RAG support + SharedSpace plan.Space `json:"-"` // Shared space } // Field the context field @@ -47,7 +49,8 @@ type FileUpload struct { // New create a new context func New(sid, cid, payload string) Context { - ctx := Context{Context: context.Background(), Sid: sid, ChatID: cid} + + ctx := Context{Context: context.Background(), Sid: sid, ChatID: cid, SharedSpace: plan.NewMemorySharedSpace()} if payload == "" { return ctx } @@ -56,6 +59,7 @@ func New(sid, cid, payload string) Context { if err != nil { log.Error("%s", err.Error()) } + return ctx } @@ -85,6 +89,13 @@ func WithTimeout(parent Context, timeout time.Duration) (Context, context.Cancel return parent, cancel } +// Release the context +func (ctx *Context) Release() { + ctx.SharedSpace.Clear() + ctx.SharedSpace = nil + ctx = nil +} + // Map the context to a map func (ctx *Context) Map() map[string]interface{} { data := map[string]interface{}{