feat: init Go module with config and in-memory note store
This commit is contained in:
@@ -0,0 +1,6 @@
|
|||||||
|
OPENAI_API_KEY=sk-your-api-key-here
|
||||||
|
OPENAI_BASE_URL=https://api.openai.com/v1
|
||||||
|
OPENAI_MODEL=gpt-4o
|
||||||
|
OPENAI_EMBED_MODEL=text-embedding-3-small
|
||||||
|
DATA_DIR=./data
|
||||||
|
PORT=8080
|
||||||
@@ -0,0 +1,30 @@
|
|||||||
|
package config
|
||||||
|
|
||||||
|
import "os"
|
||||||
|
|
||||||
|
type Config struct {
|
||||||
|
OpenAIAPIKey string
|
||||||
|
OpenAIBaseURL string
|
||||||
|
OpenAIModel string
|
||||||
|
OpenAIEmbedModel string
|
||||||
|
DataDir string
|
||||||
|
Port string
|
||||||
|
}
|
||||||
|
|
||||||
|
func Load() *Config {
|
||||||
|
return &Config{
|
||||||
|
OpenAIAPIKey: getEnv("OPENAI_API_KEY", ""),
|
||||||
|
OpenAIBaseURL: getEnv("OPENAI_BASE_URL", "https://api.openai.com/v1"),
|
||||||
|
OpenAIModel: getEnv("OPENAI_MODEL", "gpt-4o"),
|
||||||
|
OpenAIEmbedModel: getEnv("OPENAI_EMBED_MODEL", "text-embedding-3-small"),
|
||||||
|
DataDir: getEnv("DATA_DIR", "./data"),
|
||||||
|
Port: getEnv("PORT", "8080"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func getEnv(key, fallback string) string {
|
||||||
|
if v := os.Getenv(key); v != "" {
|
||||||
|
return v
|
||||||
|
}
|
||||||
|
return fallback
|
||||||
|
}
|
||||||
@@ -0,0 +1,83 @@
|
|||||||
|
package store
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"fmt"
|
||||||
|
"strings"
|
||||||
|
"sync"
|
||||||
|
)
|
||||||
|
|
||||||
|
type MemoryNoteStore struct {
|
||||||
|
mu sync.RWMutex
|
||||||
|
notes map[string]*Note
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewMemoryNoteStore() *MemoryNoteStore {
|
||||||
|
return &MemoryNoteStore{
|
||||||
|
notes: make(map[string]*Note),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *MemoryNoteStore) GetByID(_ context.Context, id string) (*Note, error) {
|
||||||
|
s.mu.RLock()
|
||||||
|
defer s.mu.RUnlock()
|
||||||
|
n, ok := s.notes[id]
|
||||||
|
if !ok {
|
||||||
|
return nil, fmt.Errorf("note not found: %s", id)
|
||||||
|
}
|
||||||
|
return n, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *MemoryNoteStore) List(_ context.Context) ([]*Note, error) {
|
||||||
|
s.mu.RLock()
|
||||||
|
defer s.mu.RUnlock()
|
||||||
|
result := make([]*Note, 0, len(s.notes))
|
||||||
|
for _, n := range s.notes {
|
||||||
|
result = append(result, n)
|
||||||
|
}
|
||||||
|
return result, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *MemoryNoteStore) Create(_ context.Context, note *Note) error {
|
||||||
|
s.mu.Lock()
|
||||||
|
defer s.mu.Unlock()
|
||||||
|
if _, exists := s.notes[note.ID]; exists {
|
||||||
|
return fmt.Errorf("note already exists: %s", note.ID)
|
||||||
|
}
|
||||||
|
s.notes[note.ID] = note
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *MemoryNoteStore) Update(_ context.Context, note *Note) error {
|
||||||
|
s.mu.Lock()
|
||||||
|
defer s.mu.Unlock()
|
||||||
|
if _, exists := s.notes[note.ID]; !exists {
|
||||||
|
return fmt.Errorf("note not found: %s", note.ID)
|
||||||
|
}
|
||||||
|
s.notes[note.ID] = note
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *MemoryNoteStore) Delete(_ context.Context, id string) error {
|
||||||
|
s.mu.Lock()
|
||||||
|
defer s.mu.Unlock()
|
||||||
|
if _, exists := s.notes[id]; !exists {
|
||||||
|
return fmt.Errorf("note not found: %s", id)
|
||||||
|
}
|
||||||
|
delete(s.notes, id)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *MemoryNoteStore) SearchByKeyword(_ context.Context, keyword string) ([]*Note, error) {
|
||||||
|
s.mu.RLock()
|
||||||
|
defer s.mu.RUnlock()
|
||||||
|
keyword = strings.ToLower(keyword)
|
||||||
|
var result []*Note
|
||||||
|
for _, n := range s.notes {
|
||||||
|
if strings.Contains(strings.ToLower(n.Title), keyword) ||
|
||||||
|
strings.Contains(strings.ToLower(n.Content), keyword) {
|
||||||
|
result = append(result, n)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return result, nil
|
||||||
|
}
|
||||||
@@ -0,0 +1,25 @@
|
|||||||
|
package store
|
||||||
|
|
||||||
|
import "time"
|
||||||
|
|
||||||
|
type Note struct {
|
||||||
|
ID string `json:"id"`
|
||||||
|
Title string `json:"title"`
|
||||||
|
Content string `json:"content"`
|
||||||
|
Tags []string `json:"tags"`
|
||||||
|
FilePath string `json:"file_path"`
|
||||||
|
CreatedAt time.Time `json:"created_at"`
|
||||||
|
UpdatedAt time.Time `json:"updated_at"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type Chunk struct {
|
||||||
|
NoteID string
|
||||||
|
Index int
|
||||||
|
Content string
|
||||||
|
Embedding []float64
|
||||||
|
}
|
||||||
|
|
||||||
|
type SearchResult struct {
|
||||||
|
Note *Note `json:"note"`
|
||||||
|
Score float64 `json:"score"`
|
||||||
|
}
|
||||||
@@ -0,0 +1,12 @@
|
|||||||
|
package store
|
||||||
|
|
||||||
|
import "context"
|
||||||
|
|
||||||
|
type NoteStore interface {
|
||||||
|
GetByID(ctx context.Context, id string) (*Note, error)
|
||||||
|
List(ctx context.Context) ([]*Note, error)
|
||||||
|
Create(ctx context.Context, note *Note) error
|
||||||
|
Update(ctx context.Context, note *Note) error
|
||||||
|
Delete(ctx context.Context, id string) error
|
||||||
|
SearchByKeyword(ctx context.Context, keyword string) ([]*Note, error)
|
||||||
|
}
|
||||||
@@ -0,0 +1,18 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"log"
|
||||||
|
|
||||||
|
"eino-test/config"
|
||||||
|
"eino-test/internal/store"
|
||||||
|
)
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
cfg := config.Load()
|
||||||
|
noteStore := store.NewMemoryNoteStore()
|
||||||
|
_ = noteStore
|
||||||
|
|
||||||
|
log.Printf("Server starting on port %s (data dir: %s)", cfg.Port, cfg.DataDir)
|
||||||
|
fmt.Println("Knowledge Assistant ready")
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user