Add full shell, replaceable Omics adapter and bilingual interaction

Implement approved specification #32 and tickets #33-#37. Keep host authentication server-verified and pin session interaction language. Compile scoped base selectors for browser compatibility and retain full gutters during CSS pruning.
This commit is contained in:
Codex
2026-09-13 14:26:39 +02:00
parent 2d1b714ebe
commit d8a29bfbdd
207 changed files with 8570 additions and 2164 deletions
@@ -1,4 +1,4 @@
// Package modelprojection renders disposable runtime adapters from the installation model catalog.
// Package modelprojection renders disposable model and public frontend installation projections.
package modelprojection
import (
@@ -16,10 +16,11 @@ import (
)
const (
CatalogFile = "catalog.json"
PiModelsFile = "pi/models.json"
PiSettingsFile = "pi/settings.json"
ComposeFile = "compose.models.yaml"
CatalogFile = "catalog.json"
PiModelsFile = "pi/models.json"
PiSettingsFile = "pi/settings.json"
ComposeFile = "compose.models.yaml"
FrontendConfigFile = "frontend/config.js"
)
var renameProjectionDirectory = os.Rename
@@ -77,6 +78,9 @@ func Render(installation config.Installation) (map[string][]byte, error) {
if err := installation.ModelCatalog.NormalizeDefaults(); err != nil {
return nil, err
}
if err := installation.Shell.NormalizeDefaults(); err != nil {
return nil, err
}
models := installation.ModelCatalog.RuntimeModels()
hasMetadata := false
for _, model := range models {
@@ -146,10 +150,21 @@ func Render(installation config.Installation) (map[string][]byte, error) {
return nil, fmt.Errorf("render Pi settings projection: %w", err)
}
fingerprint := sha256.Sum256(bytes.Join([][]byte{catalogBytes, piModelsBytes, piSettingsBytes}, nil))
composeBytes := renderCompose(installation, fmt.Sprintf("sha256:%x", fingerprint))
publicJSON, err := marshalJSON(struct {
BackendBaseURL string `json:"backendBaseUrl"`
Shell config.Shell `json:"shell"`
}{BackendBaseURL: "/api", Shell: installation.Shell})
if err != nil {
return nil, fmt.Errorf("render public frontend configuration: %w", err)
}
publicJS := append([]byte("window.__THOTHII_CONFIG__ = "), bytes.TrimSpace(publicJSON)...)
publicJS = append(publicJS, ';', '\n')
publicFingerprint := sha256.Sum256(publicJS)
composeBytes := renderCompose(installation, fmt.Sprintf("sha256:%x", fingerprint), fmt.Sprintf("sha256:%x", publicFingerprint))
return map[string][]byte{
CatalogFile: catalogBytes, PiModelsFile: piModelsBytes,
PiSettingsFile: piSettingsBytes, ComposeFile: composeBytes,
FrontendConfigFile: publicJS,
}, nil
}
@@ -210,10 +225,14 @@ func Generate(installation config.Installation) error {
}
func projectionMatches(directory string, artifacts map[string][]byte) bool {
if !projectionModeMatches(directory, 0o755) {
return false
}
for _, relative := range sortedArtifactPaths(artifacts) {
path := filepath.Join(directory, filepath.FromSlash(relative))
info, err := os.Lstat(path)
if err != nil || !info.Mode().IsRegular() {
if err != nil || !info.Mode().IsRegular() || !projectionModeMatches(path, 0o644) ||
!projectionModeMatches(filepath.Dir(path), 0o755) {
return false
}
actual, err := os.ReadFile(path)
@@ -224,6 +243,14 @@ func projectionMatches(directory string, artifacts map[string][]byte) bool {
return true
}
func projectionModeMatches(path string, mode os.FileMode) bool {
if runtime.GOOS == "windows" {
return true // POSIX mode bits do not represent Windows access controls.
}
info, err := os.Lstat(path)
return err == nil && info.Mode()&os.ModeSymlink == 0 && info.Mode().Perm() == mode
}
func writeProjectionCandidate(directory string, artifacts map[string][]byte) error {
if err := os.Chmod(directory, 0o755); err != nil {
return fmt.Errorf("protect model projection candidate: %w", err)
@@ -233,9 +260,16 @@ func writeProjectionCandidate(directory string, artifacts map[string][]byte) err
if err := os.MkdirAll(filepath.Dir(destination), 0o755); err != nil {
return fmt.Errorf("create model projection directory: %w", err)
}
if err := os.Chmod(filepath.Dir(destination), 0o755); err != nil {
return fmt.Errorf("set runtime projection directory permissions: %w", err)
}
if err := os.WriteFile(destination, artifacts[relative], 0o644); err != nil {
return fmt.Errorf("write model projection candidate: %w", err)
}
// Creation modes are filtered by the host umask; container readers have another UID.
if err := os.Chmod(destination, 0o644); err != nil {
return fmt.Errorf("set runtime projection file permissions: %w", err)
}
}
return nil
}
@@ -251,7 +285,7 @@ func absentTemporaryPath(parent string) (string, error) {
return path, nil
}
// Check returns relative artifact names whose current bytes differ from the catalog projection.
// Check returns relative artifact names whose bytes or POSIX modes differ from the projection.
func Check(installation config.Installation) ([]string, error) {
artifacts, err := Render(installation)
if err != nil {
@@ -259,8 +293,11 @@ func Check(installation config.Installation) ([]string, error) {
}
drift := make([]string, 0)
for _, relative := range sortedArtifactPaths(artifacts) {
actual, readErr := os.ReadFile(filepath.Join(installation.GeneratedDirectory(), filepath.FromSlash(relative)))
if readErr != nil || !bytes.Equal(actual, artifacts[relative]) {
path := filepath.Join(installation.GeneratedDirectory(), filepath.FromSlash(relative))
actual, readErr := os.ReadFile(path)
if readErr != nil || !bytes.Equal(actual, artifacts[relative]) ||
!projectionModeMatches(installation.GeneratedDirectory(), 0o755) ||
!projectionModeMatches(filepath.Dir(path), 0o755) || !projectionModeMatches(path, 0o644) {
drift = append(drift, relative)
}
}
@@ -275,9 +312,19 @@ func marshalJSON(value any) ([]byte, error) {
return append(contents, '\n'), nil
}
func renderCompose(installation config.Installation, fingerprint string) []byte {
func renderCompose(installation config.Installation, fingerprint, publicFingerprint string) []byte {
embedding := installation.ModelCatalog.Embedding
return []byte(fmt.Sprintf(`services:
frontend:
environment:
THT_FRONTEND_CONFIG_REVISION: %s
volumes:
- type: bind
source: %s
target: /usr/share/nginx/html/config.js
read_only: true
bind:
create_host_path: false
core:
environment:
THT_HOST_PLATFORM: %s
@@ -308,7 +355,8 @@ func renderCompose(installation config.Installation, fingerprint string) []byte
embedding-model-init:
environment:
OLLAMA_MODEL: %s
`, strconv.Quote(runtime.GOOS), strconv.Quote(fingerprint), strconv.Quote(installation.ModelCatalog.Defaults.Interaction),
`, strconv.Quote(publicFingerprint), strconv.Quote(installation.GeneratedFrontendConfigPath()),
strconv.Quote(runtime.GOOS), strconv.Quote(fingerprint), strconv.Quote(installation.ModelCatalog.Defaults.Interaction),
strconv.Quote(embedding.ID), strconv.Quote(embeddingModelName(embedding.ID)), strconv.Quote(strconv.Itoa(embedding.Dimensions)),
strconv.Quote(installation.GeneratedModelCatalogPath()), strconv.Quote(installation.GeneratedPiModelsPath()),
strconv.Quote(installation.GeneratedPiSettingsPath()), strconv.Quote(embedding.ID),