feat: project setup
This commit is contained in:
0
CONSTITUTION.md
Normal file
0
CONSTITUTION.md
Normal file
0
CONTRIBUTING.md
Normal file
0
CONTRIBUTING.md
Normal file
0
Dockerfile
Normal file
0
Dockerfile
Normal file
39
README.md
Normal file
39
README.md
Normal file
@@ -0,0 +1,39 @@
|
||||
# SpeakEasy: Know who's talking
|
||||
|
||||
## Client flow
|
||||
1. Client uploads `EPUB`
|
||||
2. Backend:
|
||||
- Stores file
|
||||
- Creates `book_id`
|
||||
- Publishes job to RabbitMQ
|
||||
3. Client subscribes:
|
||||
- `GET /books/{book_id}/stream` via WebSocket
|
||||
4. Backend:
|
||||
- Authenticates user
|
||||
- Remembers: `connection -> book_id`
|
||||
5. Worker processes text:
|
||||
- Calls Ollama
|
||||
- Stores result in Postgres
|
||||
- Emits "result ready" event
|
||||
6. Backend:
|
||||
- Receives event
|
||||
- Pushes mathing results to the subscribed client
|
||||
|
||||
### Example WebSocket message to client
|
||||
```json
|
||||
{
|
||||
"book_id": "book-abc",
|
||||
"paragraph_id": "p-432",
|
||||
"speaker": "Mary",
|
||||
"confidence": 0.87
|
||||
}
|
||||
```
|
||||
|
||||
## Worker flow
|
||||
1. Receives job from RabbitMQ
|
||||
2. Loads paragraph text
|
||||
3. Embeds paragraph
|
||||
4. Queries vector DB (pgvector)
|
||||
5. Builds prompt with retrieved content
|
||||
6. Sends prompt to Ollama
|
||||
7. Stores result
|
||||
18
ai/AI.md
Normal file
18
ai/AI.md
Normal file
@@ -0,0 +1,18 @@
|
||||
# AI
|
||||
|
||||
## FILES
|
||||
- **client** `AI Service Clients`
|
||||
- client.go `Client Interface`
|
||||
- ollama.go `Ollama HTTP Client`
|
||||
- **detector** `Speaker Detection Logic`
|
||||
- context.go `Context tracking for conversations`
|
||||
- patterns.go `Rule-based Fallback Detection`
|
||||
- speaker.go `Main Speaker Detection`
|
||||
- **models** `Model Management`
|
||||
- config.go `Model Configurations`
|
||||
- manager.go `Model Downloading/Switching`
|
||||
- registry.go `Available Models Registry`
|
||||
- **prompts** `Prompt Templates`
|
||||
- speaker_detection.go `Prompts for Speaker Identification`
|
||||
- templates.go `Prompt Template Utilities`
|
||||
|
||||
2
ai/client/client.go
Normal file
2
ai/client/client.go
Normal file
@@ -0,0 +1,2 @@
|
||||
package client
|
||||
|
||||
2
ai/client/ollama.go
Normal file
2
ai/client/ollama.go
Normal file
@@ -0,0 +1,2 @@
|
||||
package client
|
||||
|
||||
2
ai/detector/context.go
Normal file
2
ai/detector/context.go
Normal file
@@ -0,0 +1,2 @@
|
||||
package detector
|
||||
|
||||
2
ai/detector/patterns.go
Normal file
2
ai/detector/patterns.go
Normal file
@@ -0,0 +1,2 @@
|
||||
package detector
|
||||
|
||||
2
ai/detector/speaker.go
Normal file
2
ai/detector/speaker.go
Normal file
@@ -0,0 +1,2 @@
|
||||
package detector
|
||||
|
||||
2
ai/models/config.go
Normal file
2
ai/models/config.go
Normal file
@@ -0,0 +1,2 @@
|
||||
package models
|
||||
|
||||
2
ai/models/manager.go
Normal file
2
ai/models/manager.go
Normal file
@@ -0,0 +1,2 @@
|
||||
package models
|
||||
|
||||
2
ai/models/registry.go
Normal file
2
ai/models/registry.go
Normal file
@@ -0,0 +1,2 @@
|
||||
package models
|
||||
|
||||
2
ai/prompts/speaker_detection.go
Normal file
2
ai/prompts/speaker_detection.go
Normal file
@@ -0,0 +1,2 @@
|
||||
package prompts
|
||||
|
||||
2
ai/prompts/templates.go
Normal file
2
ai/prompts/templates.go
Normal file
@@ -0,0 +1,2 @@
|
||||
package prompts
|
||||
|
||||
10
app.go
Normal file
10
app.go
Normal file
@@ -0,0 +1,10 @@
|
||||
package main
|
||||
|
||||
import server_config "speakeasy/config/server"
|
||||
|
||||
func main() {
|
||||
sc := server_config.CreateNewConfig()
|
||||
sc.LoadConfig()
|
||||
println(sc.SERVER_HOST)
|
||||
println(sc.SERVER_PORT)
|
||||
}
|
||||
0
client/CLIENT.md
Normal file
0
client/CLIENT.md
Normal file
8
config/CONFIG.md
Normal file
8
config/CONFIG.md
Normal file
@@ -0,0 +1,8 @@
|
||||
# CONFIG
|
||||
|
||||
- **server** `Server Configuration`
|
||||
- config.go
|
||||
- env.go
|
||||
- **frontend** `Client Configuration`
|
||||
- config.go
|
||||
- env.go
|
||||
1
config/frontend/config.go
Normal file
1
config/frontend/config.go
Normal file
@@ -0,0 +1 @@
|
||||
package frontend
|
||||
1
config/frontend/env.go
Normal file
1
config/frontend/env.go
Normal file
@@ -0,0 +1 @@
|
||||
package frontend
|
||||
62
config/server/config.go
Normal file
62
config/server/config.go
Normal file
@@ -0,0 +1,62 @@
|
||||
package server
|
||||
|
||||
import (
|
||||
// "log"
|
||||
"speakeasy/utils/env"
|
||||
"time"
|
||||
)
|
||||
|
||||
var (
|
||||
VERSION = "dev"
|
||||
BUILD_TIME = time.Now()
|
||||
)
|
||||
|
||||
// ServerConfig is a configuration model for the backend
|
||||
type ServerConfig struct {
|
||||
PG_HOST string
|
||||
PG_USER string
|
||||
PG_PASSWORD string
|
||||
RABBITMQ_HOST string
|
||||
RABBITMQ_USER string
|
||||
RABBITMQ_PASSWORD string
|
||||
OLLAMA_HOST string
|
||||
OLLAMA_MODEL_ACCURACY string
|
||||
SERVER_HOST string
|
||||
SERVER_PORT int
|
||||
CLIENT_HOST string
|
||||
CLIENT_PORT int
|
||||
VERSION string
|
||||
BUILD_TIME time.Time
|
||||
}
|
||||
|
||||
// ModelAccuracy is a configuration option for the Ollama Model Manager
|
||||
type ModelAccuracy string
|
||||
|
||||
const (
|
||||
FAST ModelAccuracy = "FAST" // gemma:2b
|
||||
BALANCED ModelAccuracy = "BALANCED" // phi3:mini
|
||||
ACCURATE ModelAccuracy = "ACCURATE" // llama3.1:8b
|
||||
)
|
||||
|
||||
// CreateNewConfig returns an empty server config
|
||||
func CreateNewConfig() ServerConfig {
|
||||
return ServerConfig{}
|
||||
}
|
||||
|
||||
// LoadConfig loads config from environment variables
|
||||
func (c *ServerConfig) LoadConfig() {
|
||||
c.PG_HOST = env.GetString("PG_HOST", "localhost:8888")
|
||||
c.PG_USER = env.GetString("PG_USER", "speakeasy")
|
||||
c.PG_PASSWORD = env.GetString("PG_PASSWORD", "speakeasy")
|
||||
c.RABBITMQ_HOST = env.GetString("RABBITMQ_HOST", "localhost:5672")
|
||||
c.RABBITMQ_PASSWORD = env.GetString("RABBITMQ_PASSWORD", "speakeasy")
|
||||
c.RABBITMQ_USER = env.GetString("RABBITMQ_USER", "speakeasy")
|
||||
c.OLLAMA_HOST = env.GetString("OLLAMA_HOST", "localhost:11434")
|
||||
c.OLLAMA_MODEL_ACCURACY = env.GetString("OLLAMA_MODEL_ACCURACY", "BALANCED")
|
||||
c.SERVER_HOST = env.GetString("SERVER_HOST", "localhost")
|
||||
c.SERVER_PORT = env.GetInt("SERVER_PORT", 3456)
|
||||
c.CLIENT_HOST = env.GetString("CLIENT_HOST", "localhost")
|
||||
c.CLIENT_PORT = env.GetInt("CLIENT_PORT", 3457)
|
||||
c.VERSION = VERSION
|
||||
c.BUILD_TIME = BUILD_TIME
|
||||
}
|
||||
1
config/server/env.go
Normal file
1
config/server/env.go
Normal file
@@ -0,0 +1 @@
|
||||
package server
|
||||
54
docker-compose.yml
Normal file
54
docker-compose.yml
Normal file
@@ -0,0 +1,54 @@
|
||||
services:
|
||||
# Postgres with pgvector extension
|
||||
db:
|
||||
container_name: speakeasy_db
|
||||
image: pgvector/pgvector:pg18
|
||||
restart: unless-stopped
|
||||
environment:
|
||||
POSTGRES_USER: speakeasy
|
||||
POSTGRES_PASSWORD: speakeasy
|
||||
POSTGRES_DB: speakeasy
|
||||
ports:
|
||||
- "8888:5432"
|
||||
volumes:
|
||||
- pg_data:/var/lib/postgres
|
||||
- ./init:/docker-entrypoint-initdb.d
|
||||
networks:
|
||||
- speakeasy_network
|
||||
|
||||
# RabbitMQ server
|
||||
rabbitmq:
|
||||
container_name: speakeasy_rabbitmq
|
||||
image: rabbitmq:4.2.2-management-alpine
|
||||
restart: unless-stopped
|
||||
ports:
|
||||
- "5672:5672" # Default port
|
||||
- "15672:15672" # Management port
|
||||
environment:
|
||||
RABBITMQ_DEFAULT_USER: speakeasy
|
||||
RABBITMQ_DEFAULT_PASS: speakeasy
|
||||
volumes:
|
||||
- rabbitmq_data:/var/lib/rabbitmq
|
||||
networks:
|
||||
- speakeasy_network
|
||||
|
||||
# Ollama server
|
||||
ollama:
|
||||
container_name: speakeasy_ollama
|
||||
image: ollama/ollama:latest
|
||||
restart: unless-stopped
|
||||
ports:
|
||||
- "11434:11434"
|
||||
networks:
|
||||
- speakeasy_network
|
||||
volumes:
|
||||
- ollama_data:/root/.ollama
|
||||
|
||||
|
||||
volumes:
|
||||
pg_data:
|
||||
rabbitmq_data:
|
||||
ollama_data:
|
||||
|
||||
networks:
|
||||
speakeasy_network:
|
||||
1
init/init_pgvector.sql
Normal file
1
init/init_pgvector.sql
Normal file
@@ -0,0 +1 @@
|
||||
CREATE EXTENSION IF NOT EXISTS vector;
|
||||
9
models/MODELS.md
Normal file
9
models/MODELS.md
Normal file
@@ -0,0 +1,9 @@
|
||||
# MODELS
|
||||
|
||||
## FILES
|
||||
- **book** `Interfaces for Elements of the Book`
|
||||
- model.go `Structs`
|
||||
- functions.go `Helper Functions`
|
||||
- **citations** `Interfaces for Interacting With Citations`
|
||||
- model.go `Structs`
|
||||
- functions.go `Helper Functions`
|
||||
8
models/book/functions.go
Normal file
8
models/book/functions.go
Normal file
@@ -0,0 +1,8 @@
|
||||
package book
|
||||
|
||||
import "fmt"
|
||||
|
||||
// String helper method to convert dialogue struct into a string
|
||||
func (d *Dialogue) String() string {
|
||||
return fmt.Sprintf("%s", d.Content)
|
||||
}
|
||||
43
models/book/model.go
Normal file
43
models/book/model.go
Normal file
@@ -0,0 +1,43 @@
|
||||
package book
|
||||
|
||||
// Book is a model for storing chapters, author, title, and total pages
|
||||
type Book struct {
|
||||
Title string `json:"title"`
|
||||
Chapters []Chapter `json:"chapters"`
|
||||
TotalPages int `json:"total_pages"`
|
||||
}
|
||||
|
||||
// Chapter is a model that holds chaper number and pages within that chapter
|
||||
type Chapter struct {
|
||||
Index int `json:"index"` // Chapter number
|
||||
Pages []Page `json:"pages"`
|
||||
}
|
||||
|
||||
// Page is model that stores page number and paragraphs
|
||||
type Page struct {
|
||||
Index int `json:"index"`
|
||||
Paragraphs []Paragraph `json:"paragraphs"`
|
||||
}
|
||||
|
||||
// Paragraph is a model that stores paragraph number and lines
|
||||
type Paragraph struct {
|
||||
Index int `json:"index"`
|
||||
Lines []Line `json:"lines"`
|
||||
}
|
||||
|
||||
// Line is an interface for storing the content of a line
|
||||
type Line struct {
|
||||
Index int `json:"index"`
|
||||
Content any `json:"content"`
|
||||
}
|
||||
|
||||
// Dialogue is a model that stores the speaker, and text
|
||||
type Dialogue struct {
|
||||
Speaker *string `json:"speaker,omitempty"`
|
||||
Content string `json:"content"`
|
||||
}
|
||||
|
||||
// Narration is a model that stores text that isnt dialogue
|
||||
type Narration struct {
|
||||
Content string `json:"content"`
|
||||
|
||||
13
models/citations/functions.go
Normal file
13
models/citations/functions.go
Normal file
@@ -0,0 +1,13 @@
|
||||
package citations
|
||||
|
||||
import "fmt"
|
||||
|
||||
// Method to return a MLA citation as a string
|
||||
func (c *MLACitation) String() string {
|
||||
return fmt.Sprintf(`"%s" (%s, %d)`, c.Content, c.LastName, c.PageNumber)
|
||||
}
|
||||
|
||||
// Method to return a APA citation as a string
|
||||
func (c *APACitation) String() string {
|
||||
return fmt.Sprintf(`"%s" (%s, %d, p. %d)`, c.Content, c.LastName, c.Year, c.PageNumber)
|
||||
}
|
||||
17
models/citations/model.go
Normal file
17
models/citations/model.go
Normal file
@@ -0,0 +1,17 @@
|
||||
package citations
|
||||
|
||||
// MLACitation is a model for creating an inline citation in the MLA format: (Doe 23)
|
||||
type MLACitation struct {
|
||||
Content string `json:"content"`
|
||||
LastName string `json:"last_name"`
|
||||
PageNumber int `json:"page_number"`
|
||||
}
|
||||
|
||||
// APACitation is a model for creating an inline citation in the APA format: (Doe, 2020, p. 23)
|
||||
type APACitation struct {
|
||||
Content string `json:"content"`
|
||||
LastName string `json:"last_name"`
|
||||
Year int `json:"year"`
|
||||
PageNumber int `json:"page_number"`
|
||||
}
|
||||
|
||||
0
parser/PARSER.md
Normal file
0
parser/PARSER.md
Normal file
0
rabbitmq/RABBITMQ.md
Normal file
0
rabbitmq/RABBITMQ.md
Normal file
0
rag/RAG.md
Normal file
0
rag/RAG.md
Normal file
1
utils/UTILS.md
Normal file
1
utils/UTILS.md
Normal file
@@ -0,0 +1 @@
|
||||
|
||||
41
utils/env/loader.go
vendored
Normal file
41
utils/env/loader.go
vendored
Normal file
@@ -0,0 +1,41 @@
|
||||
package env
|
||||
|
||||
import (
|
||||
"os"
|
||||
"strconv"
|
||||
)
|
||||
|
||||
// GetStringOrNull retrieves a string environment variable or returns the default value if not set
|
||||
func GetString(name string, defaultValue string) string {
|
||||
value := os.Getenv(name)
|
||||
if value == "" {
|
||||
return defaultValue
|
||||
}
|
||||
return value
|
||||
}
|
||||
|
||||
// GetIntOrNull retrieves an int environment variable or returns the default value if not set
|
||||
func GetInt(name string, defaultValue int) int {
|
||||
value := os.Getenv(name)
|
||||
if value == "" {
|
||||
return defaultValue
|
||||
}
|
||||
intValue, err := strconv.Atoi(value)
|
||||
if err != nil {
|
||||
return defaultValue
|
||||
}
|
||||
return intValue
|
||||
}
|
||||
|
||||
// GetBoolOrNull retrieves a bool environment variable or returns the default value if not set
|
||||
func GetBool(name string, defaultValue bool) bool {
|
||||
value := os.Getenv(name)
|
||||
if value == "" {
|
||||
return defaultValue
|
||||
}
|
||||
boolValue, err := strconv.ParseBool(value)
|
||||
if err != nil {
|
||||
return defaultValue
|
||||
}
|
||||
return boolValue
|
||||
}
|
||||
Reference in New Issue
Block a user