svs-services-server/main.go

101 lines
3 KiB
Go

package main
import (
"embed"
"flag"
"fmt"
"html/template"
"log"
"net/http"
"os"
"strings"
"git.chandlerswift.com/chandlerswift/svs-services-server/completion"
llamaserver "git.chandlerswift.com/chandlerswift/svs-services-server/completion/llama-server"
"git.chandlerswift.com/chandlerswift/svs-services-server/completion/openrouter"
"git.chandlerswift.com/chandlerswift/svs-services-server/db"
"git.chandlerswift.com/chandlerswift/svs-services-server/site"
)
//go:embed templates/index.html
var indexTemplate string
//go:embed templates/404.html
var notFoundTemplate string
//go:embed headshots/*
var headshots embed.FS
var tmpl = template.Must(template.New("index").Parse(indexTemplate))
var notFoundTmpl = template.Must(template.New("404").Parse(notFoundTemplate))
var completionProviders map[string]completion.CompletionProvider = map[string]completion.CompletionProvider{
"openrouter": openrouter.OpenRouterProvider{
Token: os.Getenv("OPENROUTER_API_KEY"),
Model: "openai/gpt-oss-120b",
},
"llama-server": llamaserver.LlamaServerProvider{
Host: "http://localhost:8080",
Model: "gpt-oss-20b-Q4_K_M",
},
}
func parseService(host string) (h string, err error) {
parts := strings.Split(host, ".")
if len(parts) != 3 {
return "", fmt.Errorf("Unexpected number of parts in hostname; expected 3, got %v", len(parts))
}
return parts[0], nil
}
func main() {
// use flag package to parse args:
port := flag.String("port", "64434", "Port to listen on") // echo "svs" | base64 -d | od -An -t u2
address := flag.String("address", "", "Address to listen on")
completionProviderName := flag.String("completion-provider", "llama-server", "Completion provider to use (openrouter, llama-server, …)")
dbpath := flag.String("database", "svs-services.db", "Path to SQLite database file")
flag.Parse()
err := db.InitDatabase(*dbpath)
if err != nil {
log.Fatalf("Failed to initialize database: %v", err)
}
addr := fmt.Sprintf("%s:%s", *address, *port)
fmt.Println("Using completion provider", *completionProviderName)
defaultCompletionProvider := completionProviders[*completionProviderName]
// for err := defaultCompletionProvider.Health(); err != nil; err = defaultCompletionProvider.Health() {
// fmt.Println("Waiting for Llama server to be healthy... (%v)", err)
// time.Sleep(1 * time.Second)
// }
http.HandleFunc("GET /", func(w http.ResponseWriter, r *http.Request) {
service, err := parseService(r.Host)
if err != nil {
http.Error(w, err.Error(), 500)
return
}
siteData, err := site.GetSiteData(service, defaultCompletionProvider)
if err != nil {
http.Error(w, "Could not generate site", 500)
log.Printf("Getting site data for service %v: %v", service, err)
return
}
if r.URL.Path != "/" {
w.WriteHeader(http.StatusNotFound)
notFoundTmpl.Execute(w, siteData)
return
}
tmpl.Execute(w, siteData)
})
// Serve embedded headshots file
http.Handle("GET /headshots/", http.FileServer(http.FS(headshots)))
fmt.Println("Starting server on", addr)
panic(http.ListenAndServe(addr, nil))
}