Compare commits

...

2 Commits

Author SHA1 Message Date
lehel 46a4374e69
tests 2025-10-08 15:09:36 +02:00
lehel 2bd7333233
new table + translate call 2025-10-08 15:08:35 +02:00
9 changed files with 61 additions and 0 deletions

View File

@ -32,6 +32,9 @@ func (m *mockLLM) DisambiguateBestMatch(ctx context.Context, msg string, candida
func (m *mockLLM) GetEmbeddings(ctx context.Context, input string) ([]float64, error) { func (m *mockLLM) GetEmbeddings(ctx context.Context, input string) ([]float64, error) {
return m.embeddings, m.embeddingErr return m.embeddings, m.embeddingErr
} }
func (m *mockLLM) TranslateToEnglish(ctx context.Context, msg string) (string, error) {
return msg, nil
}
// --- Test VisitDB --- // --- Test VisitDB ---
type testVisitDB struct { type testVisitDB struct {

View File

@ -8,6 +8,7 @@ type Config struct {
LLM struct { LLM struct {
ExtractKeywordsPrompt string `yaml:"extract_keywords_prompt"` ExtractKeywordsPrompt string `yaml:"extract_keywords_prompt"`
DisambiguatePrompt string `yaml:"disambiguate_prompt"` DisambiguatePrompt string `yaml:"disambiguate_prompt"`
TranslatePrompt string `yaml:"translate_prompt"`
} `yaml:"llm"` } `yaml:"llm"`
} }

View File

@ -1,3 +1,4 @@
llm: llm:
extract_keywords_prompt: "You will extract structured data from the user input. Input text: {{.Message}}. Return ONLY valid minified JSON object with keys: translate (English translation of input), keyword (array of 3-5 concise English veterinary-related keywords derived strictly from the input), animal (animal mentioned or 'unknown'). Example: {\"translate\":\"dog has diarrhea\",\"keyword\":[\"diarrhea\",\"digestive\"],\"animal\":\"dog\"}. Do not add extra text, markdown, or quotes outside JSON." extract_keywords_prompt: "You will extract structured data from the user input. Input text: {{.Message}}. Return ONLY valid minified JSON object with keys: translate (English translation of input), keyword (array of 3-5 concise English veterinary-related keywords derived strictly from the input), animal (animal mentioned or 'unknown'). Example: {\"translate\":\"dog has diarrhea\",\"keyword\":[\"diarrhea\",\"digestive\"],\"animal\":\"dog\"}. Do not add extra text, markdown, or quotes outside JSON."
disambiguate_prompt: "Given candidate visit entries (JSON array): {{.Entries}} and user message: {{.Message}} choose the best matching visit's ID. Return ONLY JSON: {\"visitReason\":\"<one of the candidate IDs or empty string if none>\"}. No other text." disambiguate_prompt: "Given candidate visit entries (JSON array): {{.Entries}} and user message: {{.Message}} choose the best matching visit's ID. Return ONLY JSON: {\"visitReason\":\"<one of the candidate IDs or empty string if none>\"}. No other text."
translate_prompt: "Translate the following veterinary-related sentence to English. Input: '{{.Message}}'. Return ONLY the English translation, no extra text, no markdown, no quotes. If already English, return as is."

View File

@ -33,6 +33,9 @@ func (m *mockHandleChatLLM) DisambiguateBestMatch(ctx context.Context, msg strin
func (m *mockHandleChatLLM) GetEmbeddings(ctx context.Context, input string) ([]float64, error) { func (m *mockHandleChatLLM) GetEmbeddings(ctx context.Context, input string) ([]float64, error) {
return m.embeddings, m.embeddingErr return m.embeddings, m.embeddingErr
} }
func (m *mockHandleChatLLM) TranslateToEnglish(ctx context.Context, msg string) (string, error) {
return msg, nil
}
// mapChatRepo is an in-memory implementation of ChatRepositoryAPI for tests. // mapChatRepo is an in-memory implementation of ChatRepositoryAPI for tests.
type mapChatRepo struct { type mapChatRepo struct {

1
llm.go
View File

@ -15,6 +15,7 @@ type LLMClientAPI interface {
ExtractKeywords(ctx context.Context, message string) (map[string]interface{}, error) ExtractKeywords(ctx context.Context, message string) (map[string]interface{}, error)
DisambiguateBestMatch(ctx context.Context, message string, candidates []Visit) (string, error) DisambiguateBestMatch(ctx context.Context, message string, candidates []Visit) (string, error)
GetEmbeddings(ctx context.Context, input string) ([]float64, error) GetEmbeddings(ctx context.Context, input string) ([]float64, error)
TranslateToEnglish(ctx context.Context, message string) (string, error)
} }
// --- Format Utilities --- // --- Format Utilities ---

View File

@ -47,6 +47,10 @@ func (m *MockLLMClient) GetEmbeddings(ctx context.Context, input string) ([]floa
return []float64{0.1, 0.2, 0.3}, nil return []float64{0.1, 0.2, 0.3}, nil
} }
func (m *MockLLMClient) TranslateToEnglish(ctx context.Context, message string) (string, error) {
return message, nil
}
func TestNewLLMClientFromEnv(t *testing.T) { func TestNewLLMClientFromEnv(t *testing.T) {
tests := []struct { tests := []struct {
name string name string

View File

@ -0,0 +1,16 @@
-- +goose Up
CREATE EXTENSION IF NOT EXISTS vector;
CREATE TABLE sentence_embeddings (
id SERIAL PRIMARY KEY,
visit_id INTEGER NOT NULL,
sentence TEXT NOT NULL,
translated TEXT,
embeddings VECTOR(1536) NOT NULL,
created_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP
);
-- +goose Down
DROP TABLE IF EXISTS sentence_embeddings;
DROP EXTENSION IF EXISTS vector;

View File

@ -166,3 +166,19 @@ func (llm *OllamaClient) GetEmbeddings(ctx context.Context, input string) ([]flo
} }
return nil, fmt.Errorf("unrecognized embedding response: %.200s", string(raw)) return nil, fmt.Errorf("unrecognized embedding response: %.200s", string(raw))
} }
func (llm *OllamaClient) TranslateToEnglish(ctx context.Context, message string) (string, error) {
prompt, err := renderPrompt(appConfig.LLM.TranslatePrompt, map[string]string{"Message": message})
if err != nil {
logrus.WithError(err).Error("[CONFIG] Failed to render Translate prompt")
return "", err
}
logrus.WithField("prompt", prompt).Info("[LLM] TranslateToEnglish prompt")
resp, err := llm.ollamaCompletion(ctx, prompt, nil)
logrus.WithFields(logrus.Fields{"response": resp, "err": err}).Info("[LLM] TranslateToEnglish response")
if err != nil {
return resp, err
}
return strings.TrimSpace(resp), nil
}

View File

@ -198,3 +198,19 @@ func (llm *OpenAIClient) GetEmbeddings(ctx context.Context, input string) ([]flo
} }
return nil, fmt.Errorf("unrecognized embedding response: %.200s", string(raw)) return nil, fmt.Errorf("unrecognized embedding response: %.200s", string(raw))
} }
func (llm *OpenAIClient) TranslateToEnglish(ctx context.Context, message string) (string, error) {
prompt, err := renderPrompt(appConfig.LLM.TranslatePrompt, map[string]string{"Message": message})
if err != nil {
logrus.WithError(err).Error("[CONFIG] Failed to render Translate prompt")
return "", err
}
logrus.WithField("prompt", prompt).Info("[LLM] TranslateToEnglish prompt")
resp, err := llm.openAICompletion(ctx, prompt, nil)
logrus.WithFields(logrus.Fields{"response": resp, "err": err}).Info("[LLM] TranslateToEnglish response")
if err != nil {
return resp, err
}
return strings.TrimSpace(resp), nil
}