From c50bc358bcf5d079da4c3b9ace15d41a8568b808 Mon Sep 17 00:00:00 2001 From: Aditya Hegde Date: Mon, 31 Aug 2026 10:58:51 +0530 Subject: [PATCH] feat: make AI chat locale aware --- proto/gen/rill/runtime/v1/api.pb.go | 107 +++++++++++------- proto/gen/rill/runtime/v1/api.pb.validate.go | 4 + .../gen/rill/runtime/v1/runtime.swagger.yaml | 6 + proto/rill/runtime/v1/api.proto | 4 + runtime/ai/analyst_agent.go | 6 + runtime/ai/router_agent.go | 2 + runtime/server/chat.go | 2 + .../src/features/chat/core/conversation.ts | 2 + .../features/chat/core/input/ChatInput.svelte | 13 ++- .../src/proto/gen/rill/runtime/v1/api_pb.ts | 16 +++ .../src/runtime-client/gen/index.schemas.ts | 1 + 11 files changed, 117 insertions(+), 46 deletions(-) diff --git a/proto/gen/rill/runtime/v1/api.pb.go b/proto/gen/rill/runtime/v1/api.pb.go index ef0666651def..0dddf6e2dd0c 100644 --- a/proto/gen/rill/runtime/v1/api.pb.go +++ b/proto/gen/rill/runtime/v1/api.pb.go @@ -6265,6 +6265,8 @@ type CompleteRequest struct { ConversationId string `protobuf:"bytes,2,opt,name=conversation_id,json=conversationId,proto3" json:"conversation_id,omitempty"` // The prompt to complete. Prompt string `protobuf:"bytes,3,opt,name=prompt,proto3" json:"prompt,omitempty"` + // Locale of the user. + Locale string `protobuf:"bytes,14,opt,name=locale,proto3" json:"locale,omitempty"` // Optional agent to use for the completion. // If not set, it will infer an agent based on the prompt and conversation history. Agent string `protobuf:"bytes,10,opt,name=agent,proto3" json:"agent,omitempty"` @@ -6332,6 +6334,13 @@ func (x *CompleteRequest) GetPrompt() string { return "" } +func (x *CompleteRequest) GetLocale() string { + if x != nil { + return x.Locale + } + return "" +} + func (x *CompleteRequest) GetAgent() string { if x != nil { return x.Agent @@ -6428,6 +6437,8 @@ type CompleteStreamingRequest struct { ConversationId string `protobuf:"bytes,2,opt,name=conversation_id,json=conversationId,proto3" json:"conversation_id,omitempty"` // The prompt to complete. Prompt string `protobuf:"bytes,3,opt,name=prompt,proto3" json:"prompt,omitempty"` + // Locale of the user. + Locale string `protobuf:"bytes,14,opt,name=locale,proto3" json:"locale,omitempty"` // Optional agent to use for the completion. // If not set, it will infer an agent based on the prompt and conversation history. Agent string `protobuf:"bytes,10,opt,name=agent,proto3" json:"agent,omitempty"` @@ -6495,6 +6506,13 @@ func (x *CompleteStreamingRequest) GetPrompt() string { return "" } +func (x *CompleteStreamingRequest) GetLocale() string { + if x != nil { + return x.Locale + } + return "" +} + func (x *CompleteStreamingRequest) GetAgent() string { if x != nil { return x.Agent @@ -9714,8 +9732,8 @@ var file_rill_runtime_v1_api_proto_rawDesc = []byte{ 0x64, 0x22, 0x3b, 0x0a, 0x11, 0x4c, 0x69, 0x73, 0x74, 0x54, 0x6f, 0x6f, 0x6c, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x26, 0x0a, 0x05, 0x74, 0x6f, 0x6f, 0x6c, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x10, 0x2e, 0x72, 0x69, 0x6c, 0x6c, 0x2e, 0x61, 0x69, 0x2e, - 0x76, 0x31, 0x2e, 0x54, 0x6f, 0x6f, 0x6c, 0x52, 0x05, 0x74, 0x6f, 0x6f, 0x6c, 0x73, 0x22, 0xf4, - 0x03, 0x0a, 0x0f, 0x43, 0x6f, 0x6d, 0x70, 0x6c, 0x65, 0x74, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, + 0x76, 0x31, 0x2e, 0x54, 0x6f, 0x6f, 0x6c, 0x52, 0x05, 0x74, 0x6f, 0x6f, 0x6c, 0x73, 0x22, 0x8c, + 0x04, 0x0a, 0x0f, 0x43, 0x6f, 0x6d, 0x70, 0x6c, 0x65, 0x74, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x39, 0x0a, 0x0b, 0x69, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x42, 0x18, 0xfa, 0x42, 0x15, 0x72, 0x13, 0x32, 0x11, 0x5e, 0x5b, 0x5f, 0x5c, 0x2d, 0x61, 0x2d, 0x7a, 0x41, 0x2d, 0x5a, 0x30, 0x2d, 0x39, 0x5d, 0x2b, @@ -9723,47 +9741,50 @@ var file_rill_runtime_v1_api_proto_rawDesc = []byte{ 0x0f, 0x63, 0x6f, 0x6e, 0x76, 0x65, 0x72, 0x73, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0e, 0x63, 0x6f, 0x6e, 0x76, 0x65, 0x72, 0x73, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x49, 0x64, 0x12, 0x16, 0x0a, 0x06, 0x70, 0x72, 0x6f, 0x6d, 0x70, 0x74, - 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x70, 0x72, 0x6f, 0x6d, 0x70, 0x74, 0x12, 0x4e, - 0x0a, 0x05, 0x61, 0x67, 0x65, 0x6e, 0x74, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x09, 0x42, 0x38, 0xfa, - 0x42, 0x35, 0x72, 0x33, 0x52, 0x0d, 0x61, 0x6e, 0x61, 0x6c, 0x79, 0x73, 0x74, 0x5f, 0x61, 0x67, - 0x65, 0x6e, 0x74, 0x52, 0x0f, 0x64, 0x65, 0x76, 0x65, 0x6c, 0x6f, 0x70, 0x65, 0x72, 0x5f, 0x61, - 0x67, 0x65, 0x6e, 0x74, 0x52, 0x0e, 0x66, 0x65, 0x65, 0x64, 0x62, 0x61, 0x63, 0x6b, 0x5f, 0x61, - 0x67, 0x65, 0x6e, 0x74, 0xd0, 0x01, 0x01, 0x52, 0x05, 0x61, 0x67, 0x65, 0x6e, 0x74, 0x12, 0x58, - 0x0a, 0x15, 0x61, 0x6e, 0x61, 0x6c, 0x79, 0x73, 0x74, 0x5f, 0x61, 0x67, 0x65, 0x6e, 0x74, 0x5f, - 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x78, 0x74, 0x18, 0x0b, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x24, 0x2e, - 0x72, 0x69, 0x6c, 0x6c, 0x2e, 0x72, 0x75, 0x6e, 0x74, 0x69, 0x6d, 0x65, 0x2e, 0x76, 0x31, 0x2e, - 0x41, 0x6e, 0x61, 0x6c, 0x79, 0x73, 0x74, 0x41, 0x67, 0x65, 0x6e, 0x74, 0x43, 0x6f, 0x6e, 0x74, - 0x65, 0x78, 0x74, 0x52, 0x13, 0x61, 0x6e, 0x61, 0x6c, 0x79, 0x73, 0x74, 0x41, 0x67, 0x65, 0x6e, - 0x74, 0x43, 0x6f, 0x6e, 0x74, 0x65, 0x78, 0x74, 0x12, 0x5e, 0x0a, 0x17, 0x64, 0x65, 0x76, 0x65, - 0x6c, 0x6f, 0x70, 0x65, 0x72, 0x5f, 0x61, 0x67, 0x65, 0x6e, 0x74, 0x5f, 0x63, 0x6f, 0x6e, 0x74, - 0x65, 0x78, 0x74, 0x18, 0x0c, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x26, 0x2e, 0x72, 0x69, 0x6c, 0x6c, - 0x2e, 0x72, 0x75, 0x6e, 0x74, 0x69, 0x6d, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x44, 0x65, 0x76, 0x65, - 0x6c, 0x6f, 0x70, 0x65, 0x72, 0x41, 0x67, 0x65, 0x6e, 0x74, 0x43, 0x6f, 0x6e, 0x74, 0x65, 0x78, - 0x74, 0x52, 0x15, 0x64, 0x65, 0x76, 0x65, 0x6c, 0x6f, 0x70, 0x65, 0x72, 0x41, 0x67, 0x65, 0x6e, - 0x74, 0x43, 0x6f, 0x6e, 0x74, 0x65, 0x78, 0x74, 0x12, 0x5b, 0x0a, 0x16, 0x66, 0x65, 0x65, 0x64, - 0x62, 0x61, 0x63, 0x6b, 0x5f, 0x61, 0x67, 0x65, 0x6e, 0x74, 0x5f, 0x63, 0x6f, 0x6e, 0x74, 0x65, - 0x78, 0x74, 0x18, 0x0d, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x25, 0x2e, 0x72, 0x69, 0x6c, 0x6c, 0x2e, - 0x72, 0x75, 0x6e, 0x74, 0x69, 0x6d, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x46, 0x65, 0x65, 0x64, 0x62, - 0x61, 0x63, 0x6b, 0x41, 0x67, 0x65, 0x6e, 0x74, 0x43, 0x6f, 0x6e, 0x74, 0x65, 0x78, 0x74, 0x52, - 0x14, 0x66, 0x65, 0x65, 0x64, 0x62, 0x61, 0x63, 0x6b, 0x41, 0x67, 0x65, 0x6e, 0x74, 0x43, 0x6f, - 0x6e, 0x74, 0x65, 0x78, 0x74, 0x22, 0x71, 0x0a, 0x10, 0x43, 0x6f, 0x6d, 0x70, 0x6c, 0x65, 0x74, - 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x27, 0x0a, 0x0f, 0x63, 0x6f, 0x6e, - 0x76, 0x65, 0x72, 0x73, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, - 0x28, 0x09, 0x52, 0x0e, 0x63, 0x6f, 0x6e, 0x76, 0x65, 0x72, 0x73, 0x61, 0x74, 0x69, 0x6f, 0x6e, - 0x49, 0x64, 0x12, 0x34, 0x0a, 0x08, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x73, 0x18, 0x02, - 0x20, 0x03, 0x28, 0x0b, 0x32, 0x18, 0x2e, 0x72, 0x69, 0x6c, 0x6c, 0x2e, 0x72, 0x75, 0x6e, 0x74, - 0x69, 0x6d, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x52, 0x08, - 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x73, 0x22, 0xfd, 0x03, 0x0a, 0x18, 0x43, 0x6f, 0x6d, - 0x70, 0x6c, 0x65, 0x74, 0x65, 0x53, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x69, 0x6e, 0x67, 0x52, 0x65, - 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x39, 0x0a, 0x0b, 0x69, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x63, - 0x65, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x42, 0x18, 0xfa, 0x42, 0x15, 0x72, - 0x13, 0x32, 0x11, 0x5e, 0x5b, 0x5f, 0x5c, 0x2d, 0x61, 0x2d, 0x7a, 0x41, 0x2d, 0x5a, 0x30, 0x2d, - 0x39, 0x5d, 0x2b, 0x24, 0x52, 0x0a, 0x69, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x49, 0x64, - 0x12, 0x27, 0x0a, 0x0f, 0x63, 0x6f, 0x6e, 0x76, 0x65, 0x72, 0x73, 0x61, 0x74, 0x69, 0x6f, 0x6e, - 0x5f, 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0e, 0x63, 0x6f, 0x6e, 0x76, 0x65, - 0x72, 0x73, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x49, 0x64, 0x12, 0x16, 0x0a, 0x06, 0x70, 0x72, 0x6f, - 0x6d, 0x70, 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x70, 0x72, 0x6f, 0x6d, 0x70, - 0x74, 0x12, 0x4e, 0x0a, 0x05, 0x61, 0x67, 0x65, 0x6e, 0x74, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x09, + 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x70, 0x72, 0x6f, 0x6d, 0x70, 0x74, 0x12, 0x16, + 0x0a, 0x06, 0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x65, 0x18, 0x0e, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, + 0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x65, 0x12, 0x4e, 0x0a, 0x05, 0x61, 0x67, 0x65, 0x6e, 0x74, 0x18, + 0x0a, 0x20, 0x01, 0x28, 0x09, 0x42, 0x38, 0xfa, 0x42, 0x35, 0x72, 0x33, 0x52, 0x0d, 0x61, 0x6e, + 0x61, 0x6c, 0x79, 0x73, 0x74, 0x5f, 0x61, 0x67, 0x65, 0x6e, 0x74, 0x52, 0x0f, 0x64, 0x65, 0x76, + 0x65, 0x6c, 0x6f, 0x70, 0x65, 0x72, 0x5f, 0x61, 0x67, 0x65, 0x6e, 0x74, 0x52, 0x0e, 0x66, 0x65, + 0x65, 0x64, 0x62, 0x61, 0x63, 0x6b, 0x5f, 0x61, 0x67, 0x65, 0x6e, 0x74, 0xd0, 0x01, 0x01, 0x52, + 0x05, 0x61, 0x67, 0x65, 0x6e, 0x74, 0x12, 0x58, 0x0a, 0x15, 0x61, 0x6e, 0x61, 0x6c, 0x79, 0x73, + 0x74, 0x5f, 0x61, 0x67, 0x65, 0x6e, 0x74, 0x5f, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x78, 0x74, 0x18, + 0x0b, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x24, 0x2e, 0x72, 0x69, 0x6c, 0x6c, 0x2e, 0x72, 0x75, 0x6e, + 0x74, 0x69, 0x6d, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x41, 0x6e, 0x61, 0x6c, 0x79, 0x73, 0x74, 0x41, + 0x67, 0x65, 0x6e, 0x74, 0x43, 0x6f, 0x6e, 0x74, 0x65, 0x78, 0x74, 0x52, 0x13, 0x61, 0x6e, 0x61, + 0x6c, 0x79, 0x73, 0x74, 0x41, 0x67, 0x65, 0x6e, 0x74, 0x43, 0x6f, 0x6e, 0x74, 0x65, 0x78, 0x74, + 0x12, 0x5e, 0x0a, 0x17, 0x64, 0x65, 0x76, 0x65, 0x6c, 0x6f, 0x70, 0x65, 0x72, 0x5f, 0x61, 0x67, + 0x65, 0x6e, 0x74, 0x5f, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x78, 0x74, 0x18, 0x0c, 0x20, 0x01, 0x28, + 0x0b, 0x32, 0x26, 0x2e, 0x72, 0x69, 0x6c, 0x6c, 0x2e, 0x72, 0x75, 0x6e, 0x74, 0x69, 0x6d, 0x65, + 0x2e, 0x76, 0x31, 0x2e, 0x44, 0x65, 0x76, 0x65, 0x6c, 0x6f, 0x70, 0x65, 0x72, 0x41, 0x67, 0x65, + 0x6e, 0x74, 0x43, 0x6f, 0x6e, 0x74, 0x65, 0x78, 0x74, 0x52, 0x15, 0x64, 0x65, 0x76, 0x65, 0x6c, + 0x6f, 0x70, 0x65, 0x72, 0x41, 0x67, 0x65, 0x6e, 0x74, 0x43, 0x6f, 0x6e, 0x74, 0x65, 0x78, 0x74, + 0x12, 0x5b, 0x0a, 0x16, 0x66, 0x65, 0x65, 0x64, 0x62, 0x61, 0x63, 0x6b, 0x5f, 0x61, 0x67, 0x65, + 0x6e, 0x74, 0x5f, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x78, 0x74, 0x18, 0x0d, 0x20, 0x01, 0x28, 0x0b, + 0x32, 0x25, 0x2e, 0x72, 0x69, 0x6c, 0x6c, 0x2e, 0x72, 0x75, 0x6e, 0x74, 0x69, 0x6d, 0x65, 0x2e, + 0x76, 0x31, 0x2e, 0x46, 0x65, 0x65, 0x64, 0x62, 0x61, 0x63, 0x6b, 0x41, 0x67, 0x65, 0x6e, 0x74, + 0x43, 0x6f, 0x6e, 0x74, 0x65, 0x78, 0x74, 0x52, 0x14, 0x66, 0x65, 0x65, 0x64, 0x62, 0x61, 0x63, + 0x6b, 0x41, 0x67, 0x65, 0x6e, 0x74, 0x43, 0x6f, 0x6e, 0x74, 0x65, 0x78, 0x74, 0x22, 0x71, 0x0a, + 0x10, 0x43, 0x6f, 0x6d, 0x70, 0x6c, 0x65, 0x74, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, + 0x65, 0x12, 0x27, 0x0a, 0x0f, 0x63, 0x6f, 0x6e, 0x76, 0x65, 0x72, 0x73, 0x61, 0x74, 0x69, 0x6f, + 0x6e, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0e, 0x63, 0x6f, 0x6e, 0x76, + 0x65, 0x72, 0x73, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x49, 0x64, 0x12, 0x34, 0x0a, 0x08, 0x6d, 0x65, + 0x73, 0x73, 0x61, 0x67, 0x65, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x18, 0x2e, 0x72, + 0x69, 0x6c, 0x6c, 0x2e, 0x72, 0x75, 0x6e, 0x74, 0x69, 0x6d, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x4d, + 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x52, 0x08, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x73, + 0x22, 0x95, 0x04, 0x0a, 0x18, 0x43, 0x6f, 0x6d, 0x70, 0x6c, 0x65, 0x74, 0x65, 0x53, 0x74, 0x72, + 0x65, 0x61, 0x6d, 0x69, 0x6e, 0x67, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x39, 0x0a, + 0x0b, 0x69, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, + 0x28, 0x09, 0x42, 0x18, 0xfa, 0x42, 0x15, 0x72, 0x13, 0x32, 0x11, 0x5e, 0x5b, 0x5f, 0x5c, 0x2d, + 0x61, 0x2d, 0x7a, 0x41, 0x2d, 0x5a, 0x30, 0x2d, 0x39, 0x5d, 0x2b, 0x24, 0x52, 0x0a, 0x69, 0x6e, + 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x49, 0x64, 0x12, 0x27, 0x0a, 0x0f, 0x63, 0x6f, 0x6e, 0x76, + 0x65, 0x72, 0x73, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, + 0x09, 0x52, 0x0e, 0x63, 0x6f, 0x6e, 0x76, 0x65, 0x72, 0x73, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x49, + 0x64, 0x12, 0x16, 0x0a, 0x06, 0x70, 0x72, 0x6f, 0x6d, 0x70, 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, + 0x09, 0x52, 0x06, 0x70, 0x72, 0x6f, 0x6d, 0x70, 0x74, 0x12, 0x16, 0x0a, 0x06, 0x6c, 0x6f, 0x63, + 0x61, 0x6c, 0x65, 0x18, 0x0e, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x6c, 0x6f, 0x63, 0x61, 0x6c, + 0x65, 0x12, 0x4e, 0x0a, 0x05, 0x61, 0x67, 0x65, 0x6e, 0x74, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x09, 0x42, 0x38, 0xfa, 0x42, 0x35, 0x72, 0x33, 0x52, 0x0d, 0x61, 0x6e, 0x61, 0x6c, 0x79, 0x73, 0x74, 0x5f, 0x61, 0x67, 0x65, 0x6e, 0x74, 0x52, 0x0f, 0x64, 0x65, 0x76, 0x65, 0x6c, 0x6f, 0x70, 0x65, 0x72, 0x5f, 0x61, 0x67, 0x65, 0x6e, 0x74, 0x52, 0x0e, 0x66, 0x65, 0x65, 0x64, 0x62, 0x61, 0x63, diff --git a/proto/gen/rill/runtime/v1/api.pb.validate.go b/proto/gen/rill/runtime/v1/api.pb.validate.go index 67d3071cfe13..8c2fceddba13 100644 --- a/proto/gen/rill/runtime/v1/api.pb.validate.go +++ b/proto/gen/rill/runtime/v1/api.pb.validate.go @@ -12223,6 +12223,8 @@ func (m *CompleteRequest) validate(all bool) error { // no validation rules for Prompt + // no validation rules for Locale + if m.GetAgent() != "" { if _, ok := _CompleteRequest_Agent_InLookup[m.GetAgent()]; !ok { @@ -12584,6 +12586,8 @@ func (m *CompleteStreamingRequest) validate(all bool) error { // no validation rules for Prompt + // no validation rules for Locale + if m.GetAgent() != "" { if _, ok := _CompleteStreamingRequest_Agent_InLookup[m.GetAgent()]; !ok { diff --git a/proto/gen/rill/runtime/v1/runtime.swagger.yaml b/proto/gen/rill/runtime/v1/runtime.swagger.yaml index 425a0a899042..c9524fb0542c 100644 --- a/proto/gen/rill/runtime/v1/runtime.swagger.yaml +++ b/proto/gen/rill/runtime/v1/runtime.swagger.yaml @@ -416,6 +416,9 @@ paths: prompt: type: string description: The prompt to complete. + locale: + type: string + description: Locale of the user. agent: type: string description: |- @@ -476,6 +479,9 @@ paths: prompt: type: string description: The prompt to complete. + locale: + type: string + description: Locale of the user. agent: type: string description: |- diff --git a/proto/rill/runtime/v1/api.proto b/proto/rill/runtime/v1/api.proto index 8c0575a1520f..d42eca9129a7 100644 --- a/proto/rill/runtime/v1/api.proto +++ b/proto/rill/runtime/v1/api.proto @@ -1201,6 +1201,8 @@ message CompleteRequest { string conversation_id = 2; // The prompt to complete. string prompt = 3; + // Locale of the user. + string locale = 14; // Optional agent to use for the completion. // If not set, it will infer an agent based on the prompt and conversation history. string agent = 10 [(validate.rules).string = {ignore_empty: true, in: ["analyst_agent", "developer_agent", "feedback_agent"]}]; @@ -1229,6 +1231,8 @@ message CompleteStreamingRequest { string conversation_id = 2; // The prompt to complete. string prompt = 3; + // Locale of the user. + string locale = 14; // Optional agent to use for the completion. // If not set, it will infer an agent based on the prompt and conversation history. string agent = 10 [(validate.rules).string = {ignore_empty: true, in: ["analyst_agent", "developer_agent", "feedback_agent"]}]; diff --git a/runtime/ai/analyst_agent.go b/runtime/ai/analyst_agent.go index 40bfab552f14..ebbdee6399da 100644 --- a/runtime/ai/analyst_agent.go +++ b/runtime/ai/analyst_agent.go @@ -26,6 +26,7 @@ var _ Tool[*AnalystAgentArgs, *AnalystAgentResult] = (*AnalystAgent)(nil) type AnalystAgentArgs struct { Prompt string `json:"prompt"` + Locale string `json:"locale" yaml:"locale" jsonschema:"Optional locale for the prompt. If provided, the prompt will be localized to this locale and the response will be in the locale's language'."` Explore string `json:"explore,omitempty" yaml:"explore" jsonschema:"Optional explore dashboard name. If provided, the exploration will be limited to this dashboard."` Dimensions []string `json:"dimensions,omitempty" yaml:"dimensions" jsonschema:"Optional list of dimensions for queries. If provided, the queries will be limited to these dimensions."` Measures []string `json:"measures,omitempty" yaml:"measures" jsonschema:"Optional list of measures for queries. If provided, the queries will be limited to these measures."` @@ -258,6 +259,10 @@ func (t *AnalystAgent) userPrompt(ctx context.Context, metricsViewNames []string "max_query_limit": instanceCfg.AIMaxQueryLimit, } + if args.Locale != "" { + data["locale"] = args.Locale + } + if !args.TimeStart.IsZero() && !args.TimeEnd.IsZero() { data["time_start"] = args.TimeStart.Format(time.RFC3339) data["time_end"] = args.TimeEnd.Format(time.RFC3339) @@ -289,6 +294,7 @@ func (t *AnalystAgent) userPrompt(ctx context.Context, metricsViewNames []string // Generate the user prompt. // It carries all the per-invocation context: the current date, dashboard/report context, applied query settings, forked-session caveats, and finally the user's actual prompt. return executeTemplate(`Today's date is {{ .now.Format "Monday, January 2, 2006" }} ({{ .now.Format "2006-01-02" }}). +{{ if .locale }}User's locale is "{{ .locale }}". Make sure to use this locale's language in the response.{{ end }} {{ if .is_report }} You are operating in an automated scheduled insight report mode where you will come up with insights on your own without additional user input. diff --git a/runtime/ai/router_agent.go b/runtime/ai/router_agent.go index 1224fe8693fc..d5a726008de1 100644 --- a/runtime/ai/router_agent.go +++ b/runtime/ai/router_agent.go @@ -27,6 +27,7 @@ var _ Tool[*RouterAgentArgs, *RouterAgentResult] = (*RouterAgent)(nil) type RouterAgentArgs struct { Prompt string `json:"prompt" jsonschema:"The user's prompt to be routed."` + Locale string `json:"locale" yaml:"locale" jsonschema:"Optional locale for the prompt. If provided, the prompt will be localized to this locale and the response will be in the locale's language'."` Agent string `json:"agent,omitempty" jsonschema:"Optional agent to route the request to. If not specified, the system will infer the best agent."` AnalystAgentArgs *AnalystAgentArgs `json:"analyst_agent_args,omitempty" jsonschema:"Optional arguments to pass to the analyst agent if the selected agent is analyst_agent."` DeveloperAgentArgs *DeveloperAgentArgs `json:"developer_agent_args,omitempty" jsonschema:"Optional arguments to pass to the developer agent if the selected agent is developer_agent."` @@ -157,6 +158,7 @@ func (t *RouterAgent) Handler(ctx context.Context, args *RouterAgentArgs) (*Rout analystAgentArgs = &AnalystAgentArgs{} } analystAgentArgs.Prompt = args.Prompt + analystAgentArgs.Locale = args.Locale var res *AnalystAgentResult _, err := s.CallToolWithOptions(ctx, &CallToolOptions{ diff --git a/runtime/server/chat.go b/runtime/server/chat.go index 05a8c1c13533..9fe76dbfadec 100644 --- a/runtime/server/chat.go +++ b/runtime/server/chat.go @@ -292,6 +292,7 @@ func (s *Server) Complete(ctx context.Context, req *runtimev1.CompleteRequest) ( var res *ai.RouterAgentResult msg, err := session.CallTool(ctx, ai.RoleUser, ai.RouterAgentName, &res, ai.RouterAgentArgs{ Prompt: req.Prompt, + Locale: req.Locale, Agent: req.Agent, AnalystAgentArgs: analystAgentArgs, DeveloperAgentArgs: developerAgentArgs, @@ -447,6 +448,7 @@ func (s *Server) CompleteStreaming(req *runtimev1.CompleteStreamingRequest, stre var res *ai.RouterAgentResult msg, err := session.CallTool(ctx, ai.RoleUser, ai.RouterAgentName, &res, ai.RouterAgentArgs{ Prompt: req.Prompt, + Locale: req.Locale, Agent: req.Agent, AnalystAgentArgs: analystAgentArgs, DeveloperAgentArgs: developerAgentArgs, diff --git a/web-common/src/features/chat/core/conversation.ts b/web-common/src/features/chat/core/conversation.ts index c7d4934cbbc3..2cf39b45a765 100644 --- a/web-common/src/features/chat/core/conversation.ts +++ b/web-common/src/features/chat/core/conversation.ts @@ -340,6 +340,7 @@ export class Conversation { */ private async startStreaming(request: { prompt?: string; + locale?: string; context?: RuntimeServiceCompleteBody; feedbackAgentContext?: { targetMessageId: string; @@ -360,6 +361,7 @@ export class Conversation { ? undefined : this.conversationId, prompt: request.prompt, + locale: request.locale, agent: request.feedbackAgentContext ? ToolName.FEEDBACK_AGENT : this.agent, diff --git a/web-common/src/features/chat/core/input/ChatInput.svelte b/web-common/src/features/chat/core/input/ChatInput.svelte index f0c3ac36486a..99d62b4f2ec6 100644 --- a/web-common/src/features/chat/core/input/ChatInput.svelte +++ b/web-common/src/features/chat/core/input/ChatInput.svelte @@ -11,6 +11,7 @@ import Button from "@rilldata/web-common/components/button/Button.svelte"; import { m } from "@rilldata/web-common/lib/i18n/gen/messages"; import { ArrowUp } from "lucide-svelte"; + import { getLocale } from "@rilldata/web-common/lib/i18n/gen/runtime"; export let conversationManager: ConversationManager; export let onSend: (() => void) | undefined = undefined; @@ -46,9 +47,15 @@ // Message handling with input focus try { - await currentConversation.sendMessage(additionalContext, { - onStreamStart: () => editor.commands.setContent(""), - }); + await currentConversation.sendMessage( + { + ...additionalContext, + locale: getLocale(), + }, + { + onStreamStart: () => editor.commands.setContent(""), + }, + ); onSend?.(); } catch (error) { console.error("Failed to send message:", error); diff --git a/web-common/src/proto/gen/rill/runtime/v1/api_pb.ts b/web-common/src/proto/gen/rill/runtime/v1/api_pb.ts index 19361f15ec86..662900c73312 100644 --- a/web-common/src/proto/gen/rill/runtime/v1/api_pb.ts +++ b/web-common/src/proto/gen/rill/runtime/v1/api_pb.ts @@ -5132,6 +5132,13 @@ export class CompleteRequest extends Message$1 { */ prompt = ""; + /** + * Locale of the user. + * + * @generated from field: string locale = 14; + */ + locale = ""; + /** * Optional agent to use for the completion. * If not set, it will infer an agent based on the prompt and conversation history. @@ -5175,6 +5182,7 @@ export class CompleteRequest extends Message$1 { { no: 1, name: "instance_id", kind: "scalar", T: 9 /* ScalarType.STRING */ }, { no: 2, name: "conversation_id", kind: "scalar", T: 9 /* ScalarType.STRING */ }, { no: 3, name: "prompt", kind: "scalar", T: 9 /* ScalarType.STRING */ }, + { no: 14, name: "locale", kind: "scalar", T: 9 /* ScalarType.STRING */ }, { no: 10, name: "agent", kind: "scalar", T: 9 /* ScalarType.STRING */ }, { no: 11, name: "analyst_agent_context", kind: "message", T: AnalystAgentContext }, { no: 12, name: "developer_agent_context", kind: "message", T: DeveloperAgentContext }, @@ -5270,6 +5278,13 @@ export class CompleteStreamingRequest extends Message$1