{"repo": "ariellev/voiceline-demo", "files": {".env.example": {"669d55961890f0c8ec9296887f903204e716ec0b": {"t": 1785745158.4981067, "n": 135, "h": "ca20a03462e6a1db", "c": "PORT=9090\nDEEPSEEK_API_KEY=sk-your-deepseek-api-key-here\nDEEPGRAM_API_KEY=your-deepgram-api-key-here\nDEEPGRAM_LANGUAGE=de\nWEBHOOK_URL=\n"}, "0c53f9939efcf6527d97bd66dc47639ccdfe9fd6": {"t": 1785748292.2856095, "n": 135, "h": "ca20a03462e6a1db", "c": "PORT=9090\nDEEPSEEK_API_KEY=sk-your-deepseek-api-key-here\nDEEPGRAM_API_KEY=your-deepgram-api-key-here\nDEEPGRAM_LANGUAGE=de\nWEBHOOK_URL=\n"}, "f30ee410168c5231ccb58ac7ceeaa2c843d273bd": {"t": 1785748293.366643, "n": 147, "h": "3406ecad5da35ef6", "c": "PORT=9090\nDEEPSEEK_API_KEY=sk-your-deepseek-api-key-here\nDEEPGRAM_API_KEY=your-deepgram-api-key-here\nDEEPGRAM_VOICE_ID=aura-2-lara-de\nWEBHOOK_URL=\n"}}, "internal/deepseek/client_test.go": {"669d55961890f0c8ec9296887f903204e716ec0b": {"t": 1785745638.6197479, "n": 1439, "h": "d5a383769cd27d25", "c": "package deepseek_test\n\nimport (\n\t\"context\"\n\t\"os\"\n\t\"path/filepath\"\n\t\"testing\"\n\t\"time\"\n\n\t\"voiceline-demo/internal/deepseek\"\n)\n\nfunc TestExtract(t *testing.T) {\n\tif os.Getenv(\"DEEPSEEK_API_KEY\") == \"\" {\n\t\tt.Skip(\"DEEPSEEK_API_KEY not set\")\n\t}\n\n\ttests := []struct {\n\t\tname string\n\t\tfixture string\n\t\twantClient bool\n\t\twantSummary bool\n\t\twantActions bool\n\t}{\n\t\t{\n\t\t\tname: \"german sales transcript extracts structured notes\",\n\t\t\tfixture: \"testdata/fox_siemens_update.txt\",\n\t\t\twantClient: true,\n\t\t\twantSummary: true,\n\t\t\twantActions: true,\n\t\t},\n\t}\n\n\tfor _, tt := range tests {\n\t\tt.Run(tt.name, func(t *testing.T) {\n\t\t\tdata, err := os.ReadFile(filepath.Join(tt.fixture))\n\t\t\tif err != nil {\n\t\t\t\tt.Fatalf(\"read fixture: %v\", err)\n\t\t\t}\n\n\t\t\tclient := deepseek.New(os.Getenv(\"DEEPSEEK_API_KEY\"))\n\t\t\tctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)\n\t\t\tdefer cancel()\n\n\t\t\tnotes, err := client.Extract(ctx, string(data))\n\t\t\tif err != nil {\n\t\t\t\tt.Fatalf(\"Extract failed: %v\", err)\n\t\t\t}\n\n\t\t\tif tt.wantClient && notes.ClientName == \"\" {\n\t\t\t\tt.Error(\"expected non-empty client_name\")\n\t\t\t}\n\t\t\tif tt.wantSummary && notes.Summary == \"\" {\n\t\t\t\tt.Error(\"expected non-empty summary\")\n\t\t\t}\n\t\t\tif tt.wantActions && len(notes.ActionItems) == 0 {\n\t\t\t\tt.Error(\"expected at least one action_item\")\n\t\t\t}\n\t\t\tt.Logf(\"notes: client=%q summary=%q actions=%d\",\n\t\t\t\tnotes.ClientName, notes.Summary, len(notes.ActionItems))\n\t\t})\n\t}\n}\n"}, "0c53f9939efcf6527d97bd66dc47639ccdfe9fd6": {"t": 1785748293.1056907, "n": 1439, "h": "d5a383769cd27d25", "c": "package deepseek_test\n\nimport (\n\t\"context\"\n\t\"os\"\n\t\"path/filepath\"\n\t\"testing\"\n\t\"time\"\n\n\t\"voiceline-demo/internal/deepseek\"\n)\n\nfunc TestExtract(t *testing.T) {\n\tif os.Getenv(\"DEEPSEEK_API_KEY\") == \"\" {\n\t\tt.Skip(\"DEEPSEEK_API_KEY not set\")\n\t}\n\n\ttests := []struct {\n\t\tname string\n\t\tfixture string\n\t\twantClient bool\n\t\twantSummary bool\n\t\twantActions bool\n\t}{\n\t\t{\n\t\t\tname: \"german sales transcript extracts structured notes\",\n\t\t\tfixture: \"testdata/fox_siemens_update.txt\",\n\t\t\twantClient: true,\n\t\t\twantSummary: true,\n\t\t\twantActions: true,\n\t\t},\n\t}\n\n\tfor _, tt := range tests {\n\t\tt.Run(tt.name, func(t *testing.T) {\n\t\t\tdata, err := os.ReadFile(filepath.Join(tt.fixture))\n\t\t\tif err != nil {\n\t\t\t\tt.Fatalf(\"read fixture: %v\", err)\n\t\t\t}\n\n\t\t\tclient := deepseek.New(os.Getenv(\"DEEPSEEK_API_KEY\"))\n\t\t\tctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)\n\t\t\tdefer cancel()\n\n\t\t\tnotes, err := client.Extract(ctx, string(data))\n\t\t\tif err != nil {\n\t\t\t\tt.Fatalf(\"Extract failed: %v\", err)\n\t\t\t}\n\n\t\t\tif tt.wantClient && notes.ClientName == \"\" {\n\t\t\t\tt.Error(\"expected non-empty client_name\")\n\t\t\t}\n\t\t\tif tt.wantSummary && notes.Summary == \"\" {\n\t\t\t\tt.Error(\"expected non-empty summary\")\n\t\t\t}\n\t\t\tif tt.wantActions && len(notes.ActionItems) == 0 {\n\t\t\t\tt.Error(\"expected at least one action_item\")\n\t\t\t}\n\t\t\tt.Logf(\"notes: client=%q summary=%q actions=%d\",\n\t\t\t\tnotes.ClientName, notes.Summary, len(notes.ActionItems))\n\t\t})\n\t}\n}\n"}}, "internal/note/handler_test.go": {"669d55961890f0c8ec9296887f903204e716ec0b": {"t": 1785745651.705215, "n": 2526, "h": "a99cebdf0141f40c", "c": "package note_test\n\nimport (\n\t\"bytes\"\n\t\"encoding/json\"\n\t\"io\"\n\t\"mime/multipart\"\n\t\"net/http\"\n\t\"net/http/httptest\"\n\t\"os\"\n\t\"path/filepath\"\n\t\"testing\"\n\n\t\"github.com/gin-gonic/gin\"\n\n\t\"voiceline-demo/internal/deepgram\"\n\t\"voiceline-demo/internal/deepseek\"\n\t\"voiceline-demo/internal/note\"\n)\n\nfunc TestHandleVoiceNoteUpload(t *testing.T) {\n\tif os.Getenv(\"DEEPGRAM_API_KEY\") == \"\" || os.Getenv(\"DEEPSEEK_API_KEY\") == \"\" {\n\t\tt.Skip(\"DEEPGRAM_API_KEY and DEEPSEEK_API_KEY required\")\n\t}\n\n\ttests := []struct {\n\t\tname string\n\t\taudioFile string\n\t\twantStatus int\n\t}{\n\t\t{\n\t\t\tname: \"german audio returns structured notes\",\n\t\t\taudioFile: \"testdata/fox_siemens_update.mp3\",\n\t\t\twantStatus: http.StatusOK,\n\t\t},\n\t}\n\n\tfor _, tt := range tests {\n\t\tt.Run(tt.name, func(t *testing.T) {\n\t\t\tdg := deepgram.New(os.Getenv(\"DEEPGRAM_API_KEY\"), \"de\")\n\t\t\tds := deepseek.New(os.Getenv(\"DEEPSEEK_API_KEY\"))\n\t\t\tsvc := note.NewService(dg, ds, \"\")\n\n\t\t\tgin.SetMode(gin.TestMode)\n\t\t\trouter := gin.New()\n\t\t\trouter.POST(\"/api/v1/voice-note\", note.HandleVoiceNote(svc))\n\n\t\t\taudioData, err := os.ReadFile(filepath.Join(tt.audioFile))\n\t\t\tif err != nil {\n\t\t\t\tt.Fatalf(\"read audio: %v\", err)\n\t\t\t}\n\n\t\t\tvar buf bytes.Buffer\n\t\t\tw := multipart.NewWriter(&buf)\n\t\t\tpart, err := w.CreateFormFile(\"audio\", filepath.Base(tt.audioFile))\n\t\t\tif err != nil {\n\t\t\t\tt.Fatalf(\"create form file: %v\", err)\n\t\t\t}\n\t\t\tif _, err := io.Copy(part, bytes.NewReader(audioData)); err != nil {\n\t\t\t\tt.Fatalf(\"copy audio: %v\", err)\n\t\t\t}\n\t\t\tw.Close()\n\n\t\t\treq := httptest.NewRequest(http.MethodPost, \"/api/v1/voice-note\", &buf)\n\t\t\treq.Header.Set(\"Content-Type\", w.FormDataContentType())\n\t\t\trec := httptest.NewRecorder()\n\t\t\trouter.ServeHTTP(rec, req)\n\n\t\t\tif rec.Code != tt.wantStatus {\n\t\t\t\tt.Fatalf(\"expected %d, got %d: %s\", tt.wantStatus, rec.Code, rec.Body.String())\n\t\t\t}\n\n\t\t\tvar resp note.VoiceNoteResponse\n\t\t\tif err := json.NewDecoder(rec.Body).Decode(&resp); err != nil {\n\t\t\t\tt.Fatalf(\"decode response: %v\", err)\n\t\t\t}\n\n\t\t\tif resp.Status != \"success\" {\n\t\t\t\tt.Errorf(\"expected status success, got %q\", resp.Status)\n\t\t\t}\n\t\t\tif resp.RawTranscript == \"\" {\n\t\t\t\tt.Error(\"expected non-empty raw_transcript\")\n\t\t\t}\n\t\t\tif resp.Data.ClientName == \"\" {\n\t\t\t\tt.Error(\"expected non-empty client_name\")\n\t\t\t}\n\t\t\tif resp.Data.Summary == \"\" {\n\t\t\t\tt.Error(\"expected non-empty summary\")\n\t\t\t}\n\t\t\tif len(resp.Data.ActionItems) == 0 {\n\t\t\t\tt.Error(\"expected non-empty action_items\")\n\t\t\t}\n\n\t\t\tt.Logf(\"client=%q summary=%q actions=%d\",\n\t\t\t\tresp.Data.ClientName, resp.Data.Summary, len(resp.Data.ActionItems))\n\t\t})\n\t}\n}\n"}, "0c53f9939efcf6527d97bd66dc47639ccdfe9fd6": {"t": 1785748292.3996706, "n": 2526, "h": "a99cebdf0141f40c", "c": "package note_test\n\nimport (\n\t\"bytes\"\n\t\"encoding/json\"\n\t\"io\"\n\t\"mime/multipart\"\n\t\"net/http\"\n\t\"net/http/httptest\"\n\t\"os\"\n\t\"path/filepath\"\n\t\"testing\"\n\n\t\"github.com/gin-gonic/gin\"\n\n\t\"voiceline-demo/internal/deepgram\"\n\t\"voiceline-demo/internal/deepseek\"\n\t\"voiceline-demo/internal/note\"\n)\n\nfunc TestHandleVoiceNoteUpload(t *testing.T) {\n\tif os.Getenv(\"DEEPGRAM_API_KEY\") == \"\" || os.Getenv(\"DEEPSEEK_API_KEY\") == \"\" {\n\t\tt.Skip(\"DEEPGRAM_API_KEY and DEEPSEEK_API_KEY required\")\n\t}\n\n\ttests := []struct {\n\t\tname string\n\t\taudioFile string\n\t\twantStatus int\n\t}{\n\t\t{\n\t\t\tname: \"german audio returns structured notes\",\n\t\t\taudioFile: \"testdata/fox_siemens_update.mp3\",\n\t\t\twantStatus: http.StatusOK,\n\t\t},\n\t}\n\n\tfor _, tt := range tests {\n\t\tt.Run(tt.name, func(t *testing.T) {\n\t\t\tdg := deepgram.New(os.Getenv(\"DEEPGRAM_API_KEY\"), \"de\")\n\t\t\tds := deepseek.New(os.Getenv(\"DEEPSEEK_API_KEY\"))\n\t\t\tsvc := note.NewService(dg, ds, \"\")\n\n\t\t\tgin.SetMode(gin.TestMode)\n\t\t\trouter := gin.New()\n\t\t\trouter.POST(\"/api/v1/voice-note\", note.HandleVoiceNote(svc))\n\n\t\t\taudioData, err := os.ReadFile(filepath.Join(tt.audioFile))\n\t\t\tif err != nil {\n\t\t\t\tt.Fatalf(\"read audio: %v\", err)\n\t\t\t}\n\n\t\t\tvar buf bytes.Buffer\n\t\t\tw := multipart.NewWriter(&buf)\n\t\t\tpart, err := w.CreateFormFile(\"audio\", filepath.Base(tt.audioFile))\n\t\t\tif err != nil {\n\t\t\t\tt.Fatalf(\"create form file: %v\", err)\n\t\t\t}\n\t\t\tif _, err := io.Copy(part, bytes.NewReader(audioData)); err != nil {\n\t\t\t\tt.Fatalf(\"copy audio: %v\", err)\n\t\t\t}\n\t\t\tw.Close()\n\n\t\t\treq := httptest.NewRequest(http.MethodPost, \"/api/v1/voice-note\", &buf)\n\t\t\treq.Header.Set(\"Content-Type\", w.FormDataContentType())\n\t\t\trec := httptest.NewRecorder()\n\t\t\trouter.ServeHTTP(rec, req)\n\n\t\t\tif rec.Code != tt.wantStatus {\n\t\t\t\tt.Fatalf(\"expected %d, got %d: %s\", tt.wantStatus, rec.Code, rec.Body.String())\n\t\t\t}\n\n\t\t\tvar resp note.VoiceNoteResponse\n\t\t\tif err := json.NewDecoder(rec.Body).Decode(&resp); err != nil {\n\t\t\t\tt.Fatalf(\"decode response: %v\", err)\n\t\t\t}\n\n\t\t\tif resp.Status != \"success\" {\n\t\t\t\tt.Errorf(\"expected status success, got %q\", resp.Status)\n\t\t\t}\n\t\t\tif resp.RawTranscript == \"\" {\n\t\t\t\tt.Error(\"expected non-empty raw_transcript\")\n\t\t\t}\n\t\t\tif resp.Data.ClientName == \"\" {\n\t\t\t\tt.Error(\"expected non-empty client_name\")\n\t\t\t}\n\t\t\tif resp.Data.Summary == \"\" {\n\t\t\t\tt.Error(\"expected non-empty summary\")\n\t\t\t}\n\t\t\tif len(resp.Data.ActionItems) == 0 {\n\t\t\t\tt.Error(\"expected non-empty action_items\")\n\t\t\t}\n\n\t\t\tt.Logf(\"client=%q summary=%q actions=%d\",\n\t\t\t\tresp.Data.ClientName, resp.Data.Summary, len(resp.Data.ActionItems))\n\t\t})\n\t}\n}\n"}}, "internal/config/config.go": {"669d55961890f0c8ec9296887f903204e716ec0b": {"t": 1786068290.7003932, "n": 541, "h": "262e9d745a41817e", "c": "package config\n\nimport \"os\"\n\ntype Config struct {\n\tPort string\n\tDeepSeekKey string\n\tDeepgramKey string\n\tDeepgramLang string\n\tWebhookURL string\n}\n\nfunc Load() Config {\n\treturn Config{\n\t\tPort: getEnv(\"PORT\", \"9090\"),\n\t\tDeepSeekKey: os.Getenv(\"DEEPSEEK_API_KEY\"),\n\t\tDeepgramKey: os.Getenv(\"DEEPGRAM_API_KEY\"),\n\t\tDeepgramLang: getEnv(\"DEEPGRAM_LANGUAGE\", \"de\"),\n\t\tWebhookURL: os.Getenv(\"WEBHOOK_URL\"),\n\t}\n}\n\nfunc getEnv(key, fallback string) string {\n\tif v, ok := os.LookupEnv(key); ok {\n\t\treturn v\n\t}\n\treturn fallback\n}\n"}}}}