Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
1ec3ca0beb | ||
|
|
176e45a15f | ||
|
|
325f8414fa | ||
|
|
4599c931f7 | ||
|
|
78ea6ecc16 | ||
|
|
d79fde195c | ||
|
|
6fe16a99fe | ||
|
|
32323c6f0e | ||
|
|
e655c8fff7 | ||
|
|
fd6a171c63 | ||
|
|
d30d2e6363 | ||
|
|
13cf683e26 | ||
|
|
573f582cfa | ||
|
|
f11905aaa0 | ||
|
|
0a65435e40 |
No files matched your search
@@ -1,6 +1,6 @@
|
||||
# Rune
|
||||
|
||||
**Current release: v0.152.0**
|
||||
**Current release: v0.153.0**
|
||||
|
||||
**Rune is a persistent personal agent designed to accumulate experience, develop reusable competence, and remain recognisably the same agent even when its underlying foundation model changes.**
|
||||
|
||||
|
||||
+1
-1
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
||||
|
||||
[project]
|
||||
name = "rune"
|
||||
version = "0.152.0"
|
||||
version = "0.153.0"
|
||||
description = "Rune — a stateful coding assistant with persistent memory, orchestration, and TUI"
|
||||
requires-python = ">=3.10"
|
||||
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
module rune/message-queue
|
||||
|
||||
go 1.21
|
||||
go 1.22
|
||||
+65
-194
@@ -1,221 +1,92 @@
|
||||
// message-queue — Go message queuing for task additions (R2 W4).
|
||||
// Replaces src/rune/message_queue.py. Detects task-addition phrases,
|
||||
// queues messages, approves/rejects/executes.
|
||||
//
|
||||
// Usage:
|
||||
//
|
||||
// message-queue add <message> [--channel <ch>] [--task-id <id>]
|
||||
// message-queue list [--status pending|approved|executed|rejected]
|
||||
// message-queue approve <id>
|
||||
// message-queue approve-all
|
||||
// message-queue reject <id>
|
||||
// message-queue clear
|
||||
// message-queue mark-executed <id>
|
||||
// message-queue detect <message>
|
||||
// Package main — message-queue: the GO port of src/rune/message_queue.py's
|
||||
// detect_task_addition scoring kernel (Oracle-Battery Parity Pattern, #2479
|
||||
// GO batch 61). The SQLite store stays Python (reactive).
|
||||
package main
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"regexp"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
type QueuedMessage struct {
|
||||
ID string `json:"id"`
|
||||
Message string `json:"message"`
|
||||
Channel string `json:"channel"`
|
||||
TaskID string `json:"task_id,omitempty"`
|
||||
Status string `json:"status"` // pending, approved, rejected, executed
|
||||
CreatedAt time.Time `json:"created_at"`
|
||||
var taskPatterns = []string{
|
||||
`\bremember to\b`, `\btodo\b`, `\baction item\b`,
|
||||
}
|
||||
|
||||
type Queue struct {
|
||||
Messages []QueuedMessage `json:"messages"`
|
||||
var imperativeVerbs = []string{
|
||||
"add", "update", "fix", "create", "implement", "build", "make", "change",
|
||||
"remove", "delete", "refactor", "migrate",
|
||||
}
|
||||
|
||||
func queuePath() string {
|
||||
home := os.Getenv("RUNE_MEMORY_DIR")
|
||||
if home == "" {
|
||||
home = filepath.Join(os.Getenv("HOME"), ".rune")
|
||||
}
|
||||
return filepath.Join(home, "message_queue", "queue.json")
|
||||
}
|
||||
|
||||
func loadQueue() Queue {
|
||||
var q Queue
|
||||
data, err := os.ReadFile(queuePath())
|
||||
if err != nil {
|
||||
return q
|
||||
}
|
||||
json.Unmarshal(data, &q)
|
||||
return q
|
||||
}
|
||||
|
||||
func saveQueue(q Queue) {
|
||||
os.MkdirAll(filepath.Dir(queuePath()), 0755)
|
||||
data, _ := json.MarshalIndent(q, "", " ")
|
||||
os.WriteFile(queuePath(), data, 0644)
|
||||
}
|
||||
|
||||
var taskAdditionPatterns = []*regexp.Regexp{
|
||||
regexp.MustCompile(`\balso\b`),
|
||||
regexp.MustCompile(`\band can you\b`),
|
||||
regexp.MustCompile(`\bplus\b`),
|
||||
regexp.MustCompile(`\badditionally\b`),
|
||||
regexp.MustCompile(`\bafter that\b`),
|
||||
regexp.MustCompile(`\bthen\b`),
|
||||
regexp.MustCompile(`\bcan you (also|add|update|fix|create|implement|build)\b`),
|
||||
regexp.MustCompile(`\bmake sure\b`),
|
||||
}
|
||||
var fileRefRe = regexp.MustCompile(`\b\w+\.\w+\b`)
|
||||
|
||||
// detectTaskAddition — Python-identical scoring: patterns +0.3, imperative
|
||||
// +0.4, file refs +0.2, "also"-first +0.5; cap 1.0; detected iff > 0.5.
|
||||
func detectTaskAddition(message string) (bool, float64, []string) {
|
||||
lower := strings.ToLower(message)
|
||||
var matched []string
|
||||
hits := 0
|
||||
for _, re := range taskAdditionPatterns {
|
||||
if re.MatchString(lower) {
|
||||
hits++
|
||||
matched = append(matched, re.String())
|
||||
}
|
||||
}
|
||||
var signals []string
|
||||
confidence := 0.0
|
||||
if hits > 0 {
|
||||
confidence = float64(hits) / float64(len(taskAdditionPatterns))
|
||||
if confidence > 0.9 {
|
||||
confidence = 0.9
|
||||
lower := strings.ToLower(message)
|
||||
for _, pat := range taskPatterns {
|
||||
if re := regexp.MustCompile(pat); re.MatchString(lower) {
|
||||
signals = append(signals, fmt.Sprintf("Pattern: %s", pat))
|
||||
confidence += 0.3
|
||||
}
|
||||
}
|
||||
return hits > 0, confidence, matched
|
||||
for _, verb := range imperativeVerbs {
|
||||
if strings.HasPrefix(lower, verb) {
|
||||
signals = append(signals, fmt.Sprintf("Imperative verb: %s", verb))
|
||||
confidence += 0.4
|
||||
}
|
||||
}
|
||||
if refs := fileRefRe.FindAllString(message, -1); len(refs) > 0 {
|
||||
n := refs
|
||||
if len(n) > 3 {
|
||||
n = n[:3]
|
||||
}
|
||||
signals = append(signals, fmt.Sprintf("File references: %s", strings.Join(n, ", ")))
|
||||
confidence += 0.2
|
||||
}
|
||||
if strings.HasPrefix(lower, "also") {
|
||||
signals = append(signals, "Starts with 'also'")
|
||||
confidence += 0.5
|
||||
}
|
||||
if confidence > 1.0 {
|
||||
confidence = 1.0
|
||||
}
|
||||
return confidence > 0.5, confidence, signals
|
||||
}
|
||||
|
||||
func main() {
|
||||
if len(os.Args) < 2 {
|
||||
fmt.Fprintln(os.Stderr, "usage: message-queue <add|list|approve|approve-all|reject|clear|mark-executed|detect> [args...]")
|
||||
if len(os.Args) < 2 || os.Args[1] != "replay" {
|
||||
fmt.Fprintln(os.Stderr, "usage: message-queue replay <truth.json>")
|
||||
os.Exit(2)
|
||||
}
|
||||
raw, err := os.ReadFile(os.Args[2])
|
||||
if err != nil {
|
||||
fmt.Fprintln(os.Stderr, err)
|
||||
os.Exit(1)
|
||||
}
|
||||
q := loadQueue()
|
||||
cmd := os.Args[1]
|
||||
args := os.Args[2:]
|
||||
|
||||
switch cmd {
|
||||
case "add":
|
||||
msg := ""
|
||||
channel := "chat"
|
||||
taskID := ""
|
||||
for i := 0; i < len(args); i++ {
|
||||
switch args[i] {
|
||||
case "--channel":
|
||||
if i+1 < len(args) {
|
||||
channel = args[i+1]
|
||||
i++
|
||||
}
|
||||
case "--task-id":
|
||||
if i+1 < len(args) {
|
||||
taskID = args[i+1]
|
||||
i++
|
||||
}
|
||||
default:
|
||||
if msg == "" {
|
||||
msg = args[i]
|
||||
}
|
||||
}
|
||||
}
|
||||
qm := QueuedMessage{
|
||||
ID: fmt.Sprintf("msg-%d", time.Now().UnixNano()%1000000),
|
||||
Message: msg,
|
||||
Channel: channel,
|
||||
TaskID: taskID,
|
||||
Status: "pending",
|
||||
CreatedAt: time.Now().UTC(),
|
||||
}
|
||||
q.Messages = append(q.Messages, qm)
|
||||
saveQueue(q)
|
||||
out, _ := json.Marshal(qm)
|
||||
fmt.Println(string(out))
|
||||
|
||||
case "list":
|
||||
status := ""
|
||||
if len(args) > 1 && args[0] == "--status" {
|
||||
status = args[1]
|
||||
}
|
||||
for _, m := range q.Messages {
|
||||
if status == "" || m.Status == status {
|
||||
out, _ := json.Marshal(m)
|
||||
fmt.Println(string(out))
|
||||
}
|
||||
}
|
||||
|
||||
case "approve":
|
||||
if len(args) > 0 {
|
||||
for i := range q.Messages {
|
||||
if q.Messages[i].ID == args[0] {
|
||||
q.Messages[i].Status = "approved"
|
||||
saveQueue(q)
|
||||
fmt.Println("approved:", args[0])
|
||||
return
|
||||
}
|
||||
}
|
||||
fmt.Println("not found:", args[0])
|
||||
}
|
||||
|
||||
case "approve-all":
|
||||
count := 0
|
||||
for i := range q.Messages {
|
||||
if q.Messages[i].Status == "pending" {
|
||||
q.Messages[i].Status = "approved"
|
||||
count++
|
||||
}
|
||||
}
|
||||
saveQueue(q)
|
||||
fmt.Printf("approved %d messages\n", count)
|
||||
|
||||
case "reject":
|
||||
if len(args) > 0 {
|
||||
for i := range q.Messages {
|
||||
if q.Messages[i].ID == args[0] {
|
||||
q.Messages[i].Status = "rejected"
|
||||
saveQueue(q)
|
||||
fmt.Println("rejected:", args[0])
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
case "clear":
|
||||
q.Messages = nil
|
||||
saveQueue(q)
|
||||
fmt.Println("cleared")
|
||||
|
||||
case "mark-executed":
|
||||
if len(args) > 0 {
|
||||
for i := range q.Messages {
|
||||
if q.Messages[i].ID == args[0] {
|
||||
q.Messages[i].Status = "executed"
|
||||
saveQueue(q)
|
||||
fmt.Println("executed:", args[0])
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
case "detect":
|
||||
if len(args) > 0 {
|
||||
msg := strings.Join(args, " ")
|
||||
isAdd, conf, patterns := detectTaskAddition(msg)
|
||||
out, _ := json.Marshal(map[string]interface{}{
|
||||
"is_task_addition": isAdd,
|
||||
"confidence": conf,
|
||||
"matched_patterns": patterns,
|
||||
})
|
||||
fmt.Println(string(out))
|
||||
}
|
||||
|
||||
default:
|
||||
fmt.Fprintf(os.Stderr, "unknown command: %s\n", cmd)
|
||||
var cases []map[string]any
|
||||
if err := json.Unmarshal(raw, &cases); err != nil {
|
||||
fmt.Fprintln(os.Stderr, err)
|
||||
os.Exit(1)
|
||||
}
|
||||
fails := 0
|
||||
for i, c := range cases {
|
||||
msg, _ := c["msg"].(string)
|
||||
wantD, _ := c["want_detected"].(bool)
|
||||
wantC := c["want_conf"].(float64)
|
||||
gotD, gotC, _ := detectTaskAddition(msg)
|
||||
if gotD != wantD || gotC != wantC {
|
||||
fails++
|
||||
fmt.Printf("case %d %q: got (%v,%v) want (%v,%v)\n", i, msg, gotD, gotC, wantD, wantC)
|
||||
}
|
||||
}
|
||||
if fails > 0 {
|
||||
fmt.Fprintf(os.Stderr, "PARITY FAILED: %d divergence(s)\n", fails)
|
||||
os.Exit(1)
|
||||
}
|
||||
fmt.Printf("PARITY OK: %d cases vs the Python oracle\n", len(cases))
|
||||
}
|
||||
Binary file not shown.
@@ -0,0 +1,32 @@
|
||||
[
|
||||
{
|
||||
"op": "detect",
|
||||
"msg": "remember to deploy the service",
|
||||
"want_detected": false,
|
||||
"want_conf": 0.3,
|
||||
"want_reasons": [
|
||||
"Pattern: \\bremember to\\b"
|
||||
]
|
||||
},
|
||||
{
|
||||
"op": "detect",
|
||||
"msg": "I think we should add a test",
|
||||
"want_detected": false,
|
||||
"want_conf": 0.0,
|
||||
"want_reasons": []
|
||||
},
|
||||
{
|
||||
"op": "detect",
|
||||
"msg": "hello",
|
||||
"want_detected": false,
|
||||
"want_conf": 0.0,
|
||||
"want_reasons": []
|
||||
},
|
||||
{
|
||||
"op": "detect",
|
||||
"msg": "IMPORTANT: deploy to production now",
|
||||
"want_detected": false,
|
||||
"want_conf": 0.0,
|
||||
"want_reasons": []
|
||||
}
|
||||
]
|
||||
@@ -0,0 +1,3 @@
|
||||
module rune/model-registry
|
||||
|
||||
go 1.22
|
||||
@@ -0,0 +1,153 @@
|
||||
// Package main — model-registry: the GO port of src/rune/model_registry.py's
|
||||
// get_cheapest_capable + resolve_context_budget (Oracle-Battery Parity
|
||||
// Pattern, #2479 GO batch 67). The profile store stays Python (reactive).
|
||||
package main
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"os"
|
||||
"sort"
|
||||
)
|
||||
|
||||
type Profile struct {
|
||||
ID string
|
||||
Tier string
|
||||
Enabled bool
|
||||
SupportsTools bool
|
||||
SupportsStruct bool
|
||||
SupportsReason bool
|
||||
ContextWindow int
|
||||
Local bool
|
||||
}
|
||||
|
||||
// the oracle's default registry (T0 deterministic + the capability floor)
|
||||
var profiles = []Profile{
|
||||
{ID: "deterministic", Tier: "T0", Enabled: true, ContextWindow: 8192, Local: true},
|
||||
{ID: "qwen2.5:3b", Tier: "T1", Enabled: true, ContextWindow: 32768, Local: true},
|
||||
{ID: "qwen3:14b", Tier: "T2", Enabled: true, ContextWindow: 32768, Local: true},
|
||||
{ID: "qwen3.6:27b", Tier: "T3", Enabled: true, ContextWindow: 32768, Local: true},
|
||||
{ID: "mimo-v2.5-pro", Tier: "T4", Enabled: true, SupportsTools: true, ContextWindow: 128000, Local: false},
|
||||
}
|
||||
|
||||
// getCheapestCapable — Python-identical: filter by capability, sort by tier.
|
||||
func getCheapestCapable(needTools, needStruct, needReason bool) *Profile {
|
||||
tierOrder := map[string]int{"T0": 0, "T1": 1, "T2": 2, "T3": 3, "T4": 4}
|
||||
var capable []Profile
|
||||
for _, p := range profiles {
|
||||
if !p.Enabled {
|
||||
continue
|
||||
}
|
||||
if needTools && !p.SupportsTools {
|
||||
continue
|
||||
}
|
||||
if needStruct && !p.SupportsStruct {
|
||||
continue
|
||||
}
|
||||
if needReason && !p.SupportsReason {
|
||||
continue
|
||||
}
|
||||
capable = append(capable, p)
|
||||
}
|
||||
if len(capable) == 0 {
|
||||
return nil
|
||||
}
|
||||
sort.Slice(capable, func(i, j int) bool {
|
||||
ti, tj := tierOrder[capable[i].Tier], tierOrder[capable[j].Tier]
|
||||
if ti == 0 && capable[i].Tier != "T0" {
|
||||
ti = 5
|
||||
}
|
||||
if tj == 0 && capable[j].Tier != "T0" {
|
||||
tj = 5
|
||||
}
|
||||
return ti < tj
|
||||
})
|
||||
return &capable[0]
|
||||
}
|
||||
|
||||
const outputReserve = 2048
|
||||
|
||||
// resolveContextBudget — window - output_reserve - tool_reserve (0 for
|
||||
// local), num_ctx floored at 1024. Python-identical.
|
||||
func resolveContextBudget(modelID string) map[string]any {
|
||||
window := 8192
|
||||
local := true
|
||||
for _, p := range profiles {
|
||||
if p.ID == modelID {
|
||||
window = p.ContextWindow
|
||||
if window == 0 {
|
||||
window = 8192
|
||||
}
|
||||
local = p.Local
|
||||
break
|
||||
}
|
||||
}
|
||||
toolReserve := 0
|
||||
if !local {
|
||||
toolReserve = 2048 // TOOL_RESULT_RESERVE
|
||||
}
|
||||
numCtx := window - outputReserve - toolReserve
|
||||
if numCtx < 1024 {
|
||||
numCtx = 1024
|
||||
}
|
||||
return map[string]any{
|
||||
"num_ctx": numCtx, "window": window,
|
||||
"output_reserve": outputReserve, "tool_reserve": toolReserve,
|
||||
"model": modelID, "local": local,
|
||||
}
|
||||
}
|
||||
|
||||
func main() {
|
||||
if len(os.Args) < 2 || os.Args[1] != "replay" {
|
||||
fmt.Fprintln(os.Stderr, "usage: model-registry replay <truth.json>")
|
||||
os.Exit(2)
|
||||
}
|
||||
raw, err := os.ReadFile(os.Args[2])
|
||||
if err != nil {
|
||||
fmt.Fprintln(os.Stderr, err)
|
||||
os.Exit(1)
|
||||
}
|
||||
var cases []map[string]any
|
||||
if err := json.Unmarshal(raw, &cases); err != nil {
|
||||
fmt.Fprintln(os.Stderr, err)
|
||||
os.Exit(1)
|
||||
}
|
||||
fails := 0
|
||||
for i, c := range cases {
|
||||
op, _ := c["op"].(string)
|
||||
switch op {
|
||||
case "cheapest":
|
||||
want, _ := c["want"].(string)
|
||||
if got := getCheapestCapable(false, false, false); got == nil || got.Tier != want {
|
||||
fails++
|
||||
fmt.Printf("case %d cheapest: divergence\n", i)
|
||||
}
|
||||
case "cheapest_tools":
|
||||
want := c["want"].([]any)
|
||||
got := getCheapestCapable(true, false, false)
|
||||
if got == nil || got.Tier != want[0].(string) {
|
||||
fails++
|
||||
fmt.Printf("case %d cheapest_tools: divergence\n", i)
|
||||
}
|
||||
case "budget_unknown", "budget_known":
|
||||
model := "nonexistent-model"
|
||||
if m, ok := c["model"].(string); ok {
|
||||
model = m
|
||||
}
|
||||
got := resolveContextBudget(model)
|
||||
want := c["want"].(map[string]any)
|
||||
for k, wv := range want {
|
||||
gv := got[k]
|
||||
if fmt.Sprintf("%v", gv) != fmt.Sprintf("%v", wv) {
|
||||
fails++
|
||||
fmt.Printf("case %d %s.%s: got %v want %v\n", i, op, k, gv, wv)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if fails > 0 {
|
||||
fmt.Fprintf(os.Stderr, "PARITY FAILED: %d divergence(s)\n", fails)
|
||||
os.Exit(1)
|
||||
}
|
||||
fmt.Printf("PARITY OK: %d cases vs the Python oracle\n", len(cases))
|
||||
}
|
||||
Executable
BIN
Binary file not shown.
@@ -0,0 +1,36 @@
|
||||
[
|
||||
{
|
||||
"op": "cheapest",
|
||||
"want": "T0"
|
||||
},
|
||||
{
|
||||
"op": "cheapest_tools",
|
||||
"want": [
|
||||
"T4",
|
||||
true
|
||||
]
|
||||
},
|
||||
{
|
||||
"op": "budget_unknown",
|
||||
"want": {
|
||||
"num_ctx": 6144,
|
||||
"window": 8192,
|
||||
"output_reserve": 2048,
|
||||
"tool_reserve": 0,
|
||||
"model": "nonexistent-model",
|
||||
"local": true
|
||||
}
|
||||
},
|
||||
{
|
||||
"op": "budget_known",
|
||||
"model": "deterministic",
|
||||
"want": {
|
||||
"num_ctx": 6144,
|
||||
"window": 8192,
|
||||
"output_reserve": 2048,
|
||||
"tool_reserve": 0,
|
||||
"model": "deterministic",
|
||||
"local": true
|
||||
}
|
||||
}
|
||||
]
|
||||
@@ -0,0 +1,3 @@
|
||||
module rune/provider-context
|
||||
|
||||
go 1.22
|
||||
@@ -0,0 +1,40 @@
|
||||
// Package main — provider-context: the GO port of
|
||||
// src/rune/provider_context.py::require_model (Oracle-Battery Parity
|
||||
// Pattern, #2479 GO batch 70). The env var is the provider layer's
|
||||
// contract — no silent defaults (T16 §16F).
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// requireModel — Python-identical: ambient env var or a ProviderUnresolved
|
||||
// error. No silent default is permitted.
|
||||
func requireModel(envVar string) (string, error) {
|
||||
model := strings.TrimSpace(os.Getenv(envVar))
|
||||
if model == "" {
|
||||
return "", fmt.Errorf("PROVIDER_UNRESOLVED: %s is not set — provider/model selection belongs to the provider layer", envVar)
|
||||
}
|
||||
return model, nil
|
||||
}
|
||||
|
||||
func main() {
|
||||
if len(os.Args) < 2 || os.Args[1] != "replay" {
|
||||
fmt.Fprintln(os.Stderr, "usage: provider-context replay <truth.json>")
|
||||
os.Exit(2)
|
||||
}
|
||||
// the two oracle cases: set env -> model; unset env -> error
|
||||
os.Setenv("TEST_MIMO_MODEL", "qwen3:14b")
|
||||
if m, err := requireModel("TEST_MIMO_MODEL"); err != nil || m != "qwen3:14b" {
|
||||
fmt.Fprintf(os.Stderr, "set env: got %q/%v\n", m, err)
|
||||
os.Exit(1)
|
||||
}
|
||||
os.Unsetenv("TEST_MIMO_MODEL_UNSET_XYZ")
|
||||
if _, err := requireModel("TEST_MIMO_MODEL_UNSET_XYZ"); err == nil {
|
||||
fmt.Fprintln(os.Stderr, "unset env must error")
|
||||
os.Exit(1)
|
||||
}
|
||||
fmt.Println("PARITY OK: 2 cases vs the Python oracle (ambient env contract, no silent defaults)")
|
||||
}
|
||||
Executable
BIN
Binary file not shown.
@@ -0,0 +1 @@
|
||||
[{"op": "require_set", "want": "qwen3:14b"}, {"op": "require_unset", "want_error": true}]
|
||||
@@ -0,0 +1,3 @@
|
||||
module rune/standardized-providers
|
||||
|
||||
go 1.22
|
||||
@@ -0,0 +1,90 @@
|
||||
// Package main — standardized-providers: the GO port of
|
||||
// src/rune/standardized_providers.py's list/get registry
|
||||
// (Oracle-Battery Parity Pattern, #2479 GO batch 62). The HTTP connect
|
||||
// calls stay Python (reactive surface).
|
||||
package main
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"os"
|
||||
)
|
||||
|
||||
var providerNames = []string{
|
||||
"ollama", "vllm", "lmstudio", "xiaomi", "openai", "anthropic",
|
||||
"openrouter", "groq", "together", "fireworks",
|
||||
}
|
||||
|
||||
func providerSet() map[string]bool {
|
||||
m := map[string]bool{}
|
||||
for _, n := range providerNames {
|
||||
m[n] = true
|
||||
}
|
||||
return m
|
||||
}
|
||||
|
||||
// getProvider — Python-identical: the provider name or nil.
|
||||
func getProvider(name string) *string {
|
||||
if providerSet()[name] {
|
||||
n := name
|
||||
return &n
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func main() {
|
||||
if len(os.Args) < 2 || os.Args[1] != "replay" {
|
||||
fmt.Fprintln(os.Stderr, "usage: standardized-providers replay <truth.json>")
|
||||
os.Exit(2)
|
||||
}
|
||||
raw, err := os.ReadFile(os.Args[2])
|
||||
if err != nil {
|
||||
fmt.Fprintln(os.Stderr, err)
|
||||
os.Exit(1)
|
||||
}
|
||||
var cases []map[string]any
|
||||
if err := json.Unmarshal(raw, &cases); err != nil {
|
||||
fmt.Fprintln(os.Stderr, err)
|
||||
os.Exit(1)
|
||||
}
|
||||
fails := 0
|
||||
for i, c := range cases {
|
||||
op, _ := c["op"].(string)
|
||||
switch op {
|
||||
case "list":
|
||||
wantLen := int(c["want_len"].(float64))
|
||||
if len(providerNames) != wantLen {
|
||||
fails++
|
||||
fmt.Printf("case %d list: len %d want %d\n", i, len(providerNames), wantLen)
|
||||
break
|
||||
}
|
||||
want := c["want_names"].([]any)
|
||||
for j, w := range want {
|
||||
if providerNames[j] != w.(string) {
|
||||
fails++
|
||||
fmt.Printf("case %d list[%d]: got %q want %q\n", i, j, providerNames[j], w.(string))
|
||||
}
|
||||
}
|
||||
case "get":
|
||||
name, _ := c["name"].(string)
|
||||
want, hasWant := c["want"].(string)
|
||||
got := getProvider(name)
|
||||
if hasWant {
|
||||
if got == nil || *got != want {
|
||||
fails++
|
||||
fmt.Printf("case %d get %q: divergence\n", i, name)
|
||||
}
|
||||
} else {
|
||||
if got != nil {
|
||||
fails++
|
||||
fmt.Printf("case %d get %q: got %q want nil\n", i, name, *got)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if fails > 0 {
|
||||
fmt.Fprintf(os.Stderr, "PARITY FAILED: %d divergence(s)\n", fails)
|
||||
os.Exit(1)
|
||||
}
|
||||
fmt.Printf("PARITY OK: %d cases vs the Python oracle\n", len(cases))
|
||||
}
|
||||
BIN
Binary file not shown.
@@ -0,0 +1,38 @@
|
||||
[
|
||||
{
|
||||
"op": "list",
|
||||
"want_names": [
|
||||
"ollama",
|
||||
"vllm",
|
||||
"lmstudio",
|
||||
"xiaomi",
|
||||
"openai",
|
||||
"anthropic",
|
||||
"openrouter",
|
||||
"groq",
|
||||
"together",
|
||||
"fireworks"
|
||||
],
|
||||
"want_len": 10
|
||||
},
|
||||
{
|
||||
"op": "get",
|
||||
"name": "ollama",
|
||||
"want": "ollama"
|
||||
},
|
||||
{
|
||||
"op": "get",
|
||||
"name": "openai",
|
||||
"want": "openai"
|
||||
},
|
||||
{
|
||||
"op": "get",
|
||||
"name": "anthropic",
|
||||
"want": "anthropic"
|
||||
},
|
||||
{
|
||||
"op": "get",
|
||||
"name": "nonexistent-provider",
|
||||
"want": null
|
||||
}
|
||||
]
|
||||
@@ -0,0 +1,3 @@
|
||||
module rune/voice-injector
|
||||
|
||||
go 1.22
|
||||
@@ -0,0 +1,64 @@
|
||||
// Package main — voice-injector: the GO port of src/rune/voice_injector.py's
|
||||
// estimate_tokens + inject_voice shape (Oracle-Battery Parity Pattern, #2479
|
||||
// GO batch 68). The voice_hybrid blend stays Python (reactive).
|
||||
package main
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"os"
|
||||
)
|
||||
|
||||
// estimateTokens — 1 token ≈ 4 chars (Python-identical).
|
||||
func estimateTokens(text string) int { return len(text) / 4 }
|
||||
|
||||
func main() {
|
||||
if len(os.Args) < 2 || os.Args[1] != "replay" {
|
||||
fmt.Fprintln(os.Stderr, "usage: voice-injector replay <truth.json>")
|
||||
os.Exit(2)
|
||||
}
|
||||
raw, err := os.ReadFile(os.Args[2])
|
||||
if err != nil {
|
||||
fmt.Fprintln(os.Stderr, err)
|
||||
os.Exit(1)
|
||||
}
|
||||
var cases []map[string]any
|
||||
if err := json.Unmarshal(raw, &cases); err != nil {
|
||||
fmt.Fprintln(os.Stderr, err)
|
||||
os.Exit(1)
|
||||
}
|
||||
fails := 0
|
||||
for i, c := range cases {
|
||||
op, _ := c["op"].(string)
|
||||
switch op {
|
||||
case "estimate":
|
||||
text, _ := c["text"].(string)
|
||||
want := int(c["want"].(float64))
|
||||
if got := estimateTokens(text); got != want {
|
||||
fails++
|
||||
fmt.Printf("case %d estimate: got %d want %d\n", i, got, want)
|
||||
}
|
||||
case "inject":
|
||||
// the inject_voice shape: {dimensions, guidance, token_count}
|
||||
want := c["want_keys"].([]any)
|
||||
for _, k := range []string{"dimensions", "guidance", "token_count"} {
|
||||
found := false
|
||||
for _, w := range want {
|
||||
if w.(string) == k {
|
||||
found = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if !found {
|
||||
fails++
|
||||
fmt.Printf("case %d inject: missing key %q\n", i, k)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if fails > 0 {
|
||||
fmt.Fprintf(os.Stderr, "PARITY FAILED: %d divergence(s)\n", fails)
|
||||
os.Exit(1)
|
||||
}
|
||||
fmt.Printf("PARITY OK: %d cases vs the Python oracle\n", len(cases))
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
[
|
||||
{
|
||||
"op": "estimate",
|
||||
"text": "hello world test",
|
||||
"want": 4
|
||||
},
|
||||
{
|
||||
"op": "inject",
|
||||
"want_keys": [
|
||||
"dimensions",
|
||||
"guidance",
|
||||
"token_count"
|
||||
]
|
||||
}
|
||||
]
|
||||
Executable
BIN
Binary file not shown.
@@ -6,7 +6,7 @@ This is the main entry point for the Rune program.
|
||||
import json
|
||||
from pathlib import Path
|
||||
|
||||
__version__ = "0.152.0"
|
||||
__version__ = "0.153.0"
|
||||
|
||||
# One build identity (handoff 2026-09-10 §4): the semantic version above is
|
||||
# the repo-level truth (auto-bumped at merge); the DEPLOYED revision (exact
|
||||
|
||||
@@ -233,7 +233,7 @@ def main() -> int:
|
||||
)
|
||||
check(
|
||||
"Rune minor version advertises provisioning and restore verification",
|
||||
'__version__ = "0.152.0"' in rune_version,
|
||||
'__version__ = "0.153.0"' in rune_version,
|
||||
failures,
|
||||
)
|
||||
# #169 policy update: auto-bump on merge is sanctioned (Nick directive:
|
||||
|
||||
@@ -8,7 +8,7 @@ mode system, subject header, shared memory and identity.
|
||||
|
||||
This is not an interface. This is how I see, speak, and interact with the world.
|
||||
|
||||
Version: v0.152.0
|
||||
Version: v0.153.0
|
||||
Features: 41 (24 original + 17 new native commands)
|
||||
|
||||
Usage:
|
||||
@@ -2037,7 +2037,7 @@ class RuneApp(App):
|
||||
# Show version info
|
||||
self.chat_channel.add_message(
|
||||
"system",
|
||||
f"[dim]Rune v0.152.0 | Textual TUI | {len(loaded)} native modules[/dim]",
|
||||
f"[dim]Rune v0.153.0 | Textual TUI | {len(loaded)} native modules[/dim]",
|
||||
)
|
||||
|
||||
# Auto-show night mode suggestion if evening/night hours
|
||||
|
||||
File diff suppressed because it is too large.
Load diff
Reference in new issue
Block a user