Add full monorepo: virtual-banker, backend, frontend, docs, scripts, deployment
Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
102
backend/llm/gateway.go
Normal file
102
backend/llm/gateway.go
Normal file
@@ -0,0 +1,102 @@
|
||||
package llm
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
)
|
||||
|
||||
// Gateway provides LLM functionality
|
||||
type Gateway interface {
|
||||
Generate(ctx context.Context, prompt string, options *GenerateOptions) (*GenerateResponse, error)
|
||||
}
|
||||
|
||||
// GenerateOptions contains options for generation
|
||||
type GenerateOptions struct {
|
||||
Temperature float64
|
||||
MaxTokens int
|
||||
Tools []Tool
|
||||
TenantID string
|
||||
UserID string
|
||||
ConversationHistory []Message
|
||||
}
|
||||
|
||||
// Tool represents a callable tool/function
|
||||
type Tool struct {
|
||||
Name string
|
||||
Description string
|
||||
Parameters map[string]interface{}
|
||||
}
|
||||
|
||||
// Message represents a conversation message
|
||||
type Message struct {
|
||||
Role string // "user" or "assistant"
|
||||
Content string
|
||||
}
|
||||
|
||||
// GenerateResponse contains the LLM response
|
||||
type GenerateResponse struct {
|
||||
Text string
|
||||
Tools []ToolCall
|
||||
Emotion *Emotion
|
||||
Gestures []string
|
||||
}
|
||||
|
||||
// ToolCall represents a tool call request
|
||||
type ToolCall struct {
|
||||
Name string
|
||||
Arguments map[string]interface{}
|
||||
}
|
||||
|
||||
// Emotion represents emotional state for avatar
|
||||
type Emotion struct {
|
||||
Valence float64 // -1.0 to 1.0
|
||||
Arousal float64 // 0.0 to 1.0
|
||||
}
|
||||
|
||||
// MockLLMGateway is a mock implementation for development
|
||||
type MockLLMGateway struct{}
|
||||
|
||||
// NewMockLLMGateway creates a new mock LLM gateway
|
||||
func NewMockLLMGateway() *MockLLMGateway {
|
||||
return &MockLLMGateway{}
|
||||
}
|
||||
|
||||
// Generate generates a response using mock LLM
|
||||
func (g *MockLLMGateway) Generate(ctx context.Context, prompt string, options *GenerateOptions) (*GenerateResponse, error) {
|
||||
// Mock implementation
|
||||
return &GenerateResponse{
|
||||
Text: "I understand. How can I assist you with your banking needs today?",
|
||||
Emotion: &Emotion{
|
||||
Valence: 0.5,
|
||||
Arousal: 0.3,
|
||||
},
|
||||
Gestures: []string{"nod"},
|
||||
}, nil
|
||||
}
|
||||
|
||||
// OpenAIGateway integrates with OpenAI (example - requires API key)
|
||||
type OpenAIGateway struct {
|
||||
apiKey string
|
||||
model string
|
||||
}
|
||||
|
||||
// NewOpenAIGateway creates a new OpenAI gateway
|
||||
func NewOpenAIGateway(apiKey, model string) *OpenAIGateway {
|
||||
return &OpenAIGateway{
|
||||
apiKey: apiKey,
|
||||
model: model,
|
||||
}
|
||||
}
|
||||
|
||||
// Generate generates using OpenAI API
|
||||
func (g *OpenAIGateway) Generate(ctx context.Context, prompt string, options *GenerateOptions) (*GenerateResponse, error) {
|
||||
// TODO: Implement OpenAI API integration
|
||||
// This would involve:
|
||||
// 1. Building the prompt with system message, conversation history
|
||||
// 2. Adding tool definitions if tools are provided
|
||||
// 3. Making API call to OpenAI
|
||||
// 4. Parsing response and extracting tool calls
|
||||
// 5. Mapping to GenerateResponse format
|
||||
return nil, fmt.Errorf("not implemented - requires OpenAI API integration")
|
||||
}
|
||||
|
||||
124
backend/llm/prompt.go
Normal file
124
backend/llm/prompt.go
Normal file
@@ -0,0 +1,124 @@
|
||||
package llm
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// BuildPrompt builds a prompt from components
|
||||
func BuildPrompt(tenantConfig *TenantConfig, conversationHistory []Message, userInput string, retrievedDocs []RetrievedDoc) string {
|
||||
var parts []string
|
||||
|
||||
// System message
|
||||
systemMsg := buildSystemMessage(tenantConfig)
|
||||
parts = append(parts, systemMsg)
|
||||
|
||||
// Retrieved documents (RAG context)
|
||||
if len(retrievedDocs) > 0 {
|
||||
parts = append(parts, "\n## Context:")
|
||||
for i, doc := range retrievedDocs {
|
||||
parts = append(parts, fmt.Sprintf("\n[Document %d]", i+1))
|
||||
parts = append(parts, fmt.Sprintf("Title: %s", doc.Title))
|
||||
parts = append(parts, fmt.Sprintf("Content: %s", doc.Content))
|
||||
if doc.URL != "" {
|
||||
parts = append(parts, fmt.Sprintf("Source: %s", doc.URL))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Conversation history
|
||||
if len(conversationHistory) > 0 {
|
||||
parts = append(parts, "\n## Conversation History:")
|
||||
for _, msg := range conversationHistory {
|
||||
parts = append(parts, fmt.Sprintf("%s: %s", strings.Title(msg.Role), msg.Content))
|
||||
}
|
||||
}
|
||||
|
||||
// Current user input
|
||||
parts = append(parts, fmt.Sprintf("\n## User: %s", userInput))
|
||||
parts = append(parts, "\n## Assistant:")
|
||||
|
||||
return strings.Join(parts, "\n")
|
||||
}
|
||||
|
||||
// TenantConfig holds tenant-specific configuration
|
||||
type TenantConfig struct {
|
||||
Greeting string
|
||||
Tone string // "professional", "friendly", "formal"
|
||||
Disclaimers []string
|
||||
AllowedTools []string
|
||||
}
|
||||
|
||||
// RetrievedDoc represents a retrieved document from RAG
|
||||
type RetrievedDoc struct {
|
||||
Title string
|
||||
Content string
|
||||
URL string
|
||||
Score float64
|
||||
}
|
||||
|
||||
// BuildPromptWithRAG builds a prompt with RAG context
|
||||
func BuildPromptWithRAG(tenantConfig *TenantConfig, conversationHistory []Message, userInput string, retrievedDocs []RetrievedDoc) string {
|
||||
var parts []string
|
||||
|
||||
// System message
|
||||
systemMsg := buildSystemMessage(tenantConfig)
|
||||
parts = append(parts, systemMsg)
|
||||
|
||||
// Retrieved documents (RAG context)
|
||||
if len(retrievedDocs) > 0 {
|
||||
parts = append(parts, "\n## Context:")
|
||||
for i, doc := range retrievedDocs {
|
||||
parts = append(parts, fmt.Sprintf("\n[Document %d]", i+1))
|
||||
parts = append(parts, fmt.Sprintf("Title: %s", doc.Title))
|
||||
parts = append(parts, fmt.Sprintf("Content: %s", doc.Content))
|
||||
if doc.URL != "" {
|
||||
parts = append(parts, fmt.Sprintf("Source: %s", doc.URL))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Conversation history
|
||||
if len(conversationHistory) > 0 {
|
||||
parts = append(parts, "\n## Conversation History:")
|
||||
for _, msg := range conversationHistory {
|
||||
parts = append(parts, fmt.Sprintf("%s: %s", strings.Title(msg.Role), msg.Content))
|
||||
}
|
||||
}
|
||||
|
||||
// Current user input
|
||||
parts = append(parts, fmt.Sprintf("\n## User: %s", userInput))
|
||||
parts = append(parts, "\n## Assistant:")
|
||||
|
||||
return strings.Join(parts, "\n")
|
||||
}
|
||||
|
||||
// buildSystemMessage builds the system message
|
||||
func buildSystemMessage(config *TenantConfig) string {
|
||||
var parts []string
|
||||
|
||||
parts = append(parts, "You are a helpful Virtual Banker assistant.")
|
||||
|
||||
if config.Tone != "" {
|
||||
parts = append(parts, fmt.Sprintf("Your tone should be %s.", config.Tone))
|
||||
}
|
||||
|
||||
if len(config.Disclaimers) > 0 {
|
||||
parts = append(parts, "\nImportant disclaimers:")
|
||||
for _, disclaimer := range config.Disclaimers {
|
||||
parts = append(parts, fmt.Sprintf("- %s", disclaimer))
|
||||
}
|
||||
}
|
||||
|
||||
if len(config.AllowedTools) > 0 {
|
||||
parts = append(parts, "\nYou have access to the following tools:")
|
||||
for _, tool := range config.AllowedTools {
|
||||
parts = append(parts, fmt.Sprintf("- %s", tool))
|
||||
}
|
||||
}
|
||||
|
||||
parts = append(parts, "\nAlways be helpful, accurate, and respectful.")
|
||||
parts = append(parts, "If you don't know something, say so and offer to help find the answer.")
|
||||
|
||||
return strings.Join(parts, "\n")
|
||||
}
|
||||
Reference in New Issue
Block a user