@geravant/sinain 1.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (53) hide show
  1. package/README.md +183 -0
  2. package/index.ts +2096 -0
  3. package/install.js +155 -0
  4. package/openclaw.plugin.json +59 -0
  5. package/package.json +21 -0
  6. package/sinain-memory/common.py +403 -0
  7. package/sinain-memory/demo_knowledge_transfer.sh +85 -0
  8. package/sinain-memory/embedder.py +268 -0
  9. package/sinain-memory/eval/__init__.py +0 -0
  10. package/sinain-memory/eval/assertions.py +288 -0
  11. package/sinain-memory/eval/judges/__init__.py +0 -0
  12. package/sinain-memory/eval/judges/base_judge.py +61 -0
  13. package/sinain-memory/eval/judges/curation_judge.py +46 -0
  14. package/sinain-memory/eval/judges/insight_judge.py +48 -0
  15. package/sinain-memory/eval/judges/mining_judge.py +42 -0
  16. package/sinain-memory/eval/judges/signal_judge.py +45 -0
  17. package/sinain-memory/eval/schemas.py +247 -0
  18. package/sinain-memory/eval_delta.py +109 -0
  19. package/sinain-memory/eval_reporter.py +642 -0
  20. package/sinain-memory/feedback_analyzer.py +221 -0
  21. package/sinain-memory/git_backup.sh +19 -0
  22. package/sinain-memory/insight_synthesizer.py +181 -0
  23. package/sinain-memory/memory/2026-03-01.md +11 -0
  24. package/sinain-memory/memory/playbook-archive/sinain-playbook-2026-03-01-1418.md +15 -0
  25. package/sinain-memory/memory/playbook-logs/2026-03-01.jsonl +1 -0
  26. package/sinain-memory/memory/sinain-playbook.md +21 -0
  27. package/sinain-memory/memory-config.json +39 -0
  28. package/sinain-memory/memory_miner.py +183 -0
  29. package/sinain-memory/module_manager.py +695 -0
  30. package/sinain-memory/playbook_curator.py +225 -0
  31. package/sinain-memory/requirements.txt +3 -0
  32. package/sinain-memory/signal_analyzer.py +141 -0
  33. package/sinain-memory/test_local.py +402 -0
  34. package/sinain-memory/tests/__init__.py +0 -0
  35. package/sinain-memory/tests/conftest.py +189 -0
  36. package/sinain-memory/tests/test_curator_helpers.py +94 -0
  37. package/sinain-memory/tests/test_embedder.py +210 -0
  38. package/sinain-memory/tests/test_extract_json.py +124 -0
  39. package/sinain-memory/tests/test_feedback_computation.py +121 -0
  40. package/sinain-memory/tests/test_miner_helpers.py +71 -0
  41. package/sinain-memory/tests/test_module_management.py +458 -0
  42. package/sinain-memory/tests/test_parsers.py +96 -0
  43. package/sinain-memory/tests/test_tick_evaluator.py +430 -0
  44. package/sinain-memory/tests/test_triple_extractor.py +255 -0
  45. package/sinain-memory/tests/test_triple_ingest.py +191 -0
  46. package/sinain-memory/tests/test_triple_migrate.py +138 -0
  47. package/sinain-memory/tests/test_triplestore.py +248 -0
  48. package/sinain-memory/tick_evaluator.py +392 -0
  49. package/sinain-memory/triple_extractor.py +402 -0
  50. package/sinain-memory/triple_ingest.py +290 -0
  51. package/sinain-memory/triple_migrate.py +275 -0
  52. package/sinain-memory/triple_query.py +184 -0
  53. package/sinain-memory/triplestore.py +498 -0
@@ -0,0 +1,221 @@
1
+ #!/usr/bin/env python3
2
+ """Phase 3 Step 2/2b: Feedback Analyzer — score feedback + compute effectiveness.
3
+
4
+ Fully mechanical: effectiveness, feedback scores, directive, and interpretation
5
+ are all computed from log data without an LLM call. The previous gpt-5-nano
6
+ integration was removed after 60+ ticks of identical static output (avg 0.4,
7
+ directive 'stability') regardless of context.
8
+
9
+ Usage:
10
+ python3 feedback_analyzer.py --memory-dir memory/ --session-summary "..."
11
+ """
12
+
13
+ import argparse
14
+ import json
15
+ import sys
16
+
17
+ from common import (
18
+ output_json,
19
+ read_recent_logs,
20
+ )
21
+
22
+
23
+ def compute_effectiveness(logs: list[dict]) -> dict:
24
+ """Mechanically compute effectiveness from playbook-log entries.
25
+
26
+ - outputs: ticks where Step 5 produced output (not skipped)
27
+ - positive: ticks where output was followed by avg compositeScore > 0.2
28
+ - negative: ticks where output was followed by avg compositeScore < -0.1
29
+ - neutral: remainder
30
+ - rate: positive / outputs
31
+ """
32
+ output_ticks = [e for e in logs if not e.get("skipped", True)]
33
+ outputs = len(output_ticks)
34
+
35
+ if outputs == 0:
36
+ return {"outputs": 0, "positive": 0, "negative": 0, "neutral": 0, "rate": 0.0}
37
+
38
+ positive = 0
39
+ negative = 0
40
+ neutral = 0
41
+
42
+ # Sort by timestamp for sequential analysis
43
+ sorted_logs = sorted(logs, key=lambda e: e.get("ts", ""))
44
+
45
+ for i, entry in enumerate(sorted_logs):
46
+ if entry.get("skipped", True):
47
+ continue
48
+ # Look at next tick's feedback
49
+ if i + 1 < len(sorted_logs):
50
+ next_entry = sorted_logs[i + 1]
51
+ feedback = next_entry.get("feedbackScores", {})
52
+ avg = feedback.get("avg", 0)
53
+ if avg > 0.2:
54
+ positive += 1
55
+ elif avg < -0.1:
56
+ negative += 1
57
+ else:
58
+ neutral += 1
59
+ else:
60
+ neutral += 1 # No next tick yet
61
+
62
+ rate = positive / outputs if outputs > 0 else 0.0
63
+ return {
64
+ "outputs": outputs,
65
+ "positive": positive,
66
+ "negative": negative,
67
+ "neutral": neutral,
68
+ "rate": round(rate, 2),
69
+ }
70
+
71
+
72
+ def extract_feedback_scores(logs: list[dict]) -> dict:
73
+ """Extract composite scores with time-decay weighting."""
74
+ all_scores = []
75
+ high_patterns = []
76
+ low_patterns = []
77
+
78
+ for i, entry in enumerate(logs): # logs are newest-first
79
+ feedback = entry.get("feedbackScores", {})
80
+ avg = feedback.get("avg")
81
+ if avg is not None:
82
+ # Decay: recent entries weighted more (1.0 for newest, 0.3 for oldest)
83
+ weight = max(0.3, 1.0 - (i / max(len(logs), 1)) * 0.7)
84
+ all_scores.append((avg, weight))
85
+
86
+ for h in feedback.get("high", []):
87
+ if h not in high_patterns: # deduplicate
88
+ high_patterns.append(h)
89
+ for lo in feedback.get("low", []):
90
+ if lo not in low_patterns: # deduplicate
91
+ low_patterns.append(lo)
92
+
93
+ if all_scores:
94
+ weighted_sum = sum(s * w for s, w in all_scores)
95
+ weight_sum = sum(w for _, w in all_scores)
96
+ avg_score = round(weighted_sum / weight_sum, 2)
97
+ else:
98
+ avg_score = 0
99
+
100
+ return {
101
+ "avg": avg_score,
102
+ "high": high_patterns[:5],
103
+ "low": low_patterns[:5],
104
+ }
105
+
106
+
107
+ def determine_directive(effectiveness: dict) -> str:
108
+ """Determine curate directive from effectiveness metrics."""
109
+ if effectiveness["outputs"] < 5:
110
+ return "insufficient_data"
111
+ rate = effectiveness["rate"]
112
+ if rate < 0.4:
113
+ return "aggressive_prune"
114
+ elif rate > 0.7:
115
+ return "stability"
116
+ return "normal"
117
+
118
+
119
+ def compute_score_trend(logs: list[dict]) -> str:
120
+ """Detect score trend from recent logs: rising, falling, or flat."""
121
+ scores = []
122
+ for entry in sorted(logs, key=lambda e: e.get("ts", "")):
123
+ fb = entry.get("feedbackScores", {})
124
+ avg = fb.get("avg")
125
+ if avg is not None:
126
+ scores.append(avg)
127
+ if len(scores) < 3:
128
+ return "insufficient"
129
+ # Compare first third vs last third
130
+ third = max(1, len(scores) // 3)
131
+ early_avg = sum(scores[:third]) / third
132
+ late_avg = sum(scores[-third:]) / third
133
+ delta = late_avg - early_avg
134
+ if delta > 0.1:
135
+ return "rising"
136
+ elif delta < -0.1:
137
+ return "falling"
138
+ return "flat"
139
+
140
+
141
+ def generate_interpretation(
142
+ feedback_scores: dict, effectiveness: dict, directive: str, logs: list[dict]
143
+ ) -> str:
144
+ """Heuristic interpretation from mechanical metrics — no LLM needed.
145
+
146
+ Replaces the gpt-5-nano call that was returning static output (avg 0.4,
147
+ directive 'stability') regardless of context across 60+ ticks.
148
+ """
149
+ parts = []
150
+
151
+ trend = compute_score_trend(logs)
152
+ avg = feedback_scores.get("avg", 0)
153
+ rate = effectiveness.get("rate", 0)
154
+ outputs = effectiveness.get("outputs", 0)
155
+
156
+ # Score summary
157
+ if trend == "rising":
158
+ parts.append(f"Scores trending up (avg {avg})")
159
+ elif trend == "falling":
160
+ parts.append(f"Scores trending down (avg {avg}) — review recent patterns")
161
+ elif trend == "flat":
162
+ parts.append(f"Scores flat at avg {avg}")
163
+ else:
164
+ parts.append(f"Too few data points for trend (avg {avg})")
165
+
166
+ # Effectiveness summary
167
+ pos = effectiveness.get("positive", 0)
168
+ neg = effectiveness.get("negative", 0)
169
+ if outputs > 0:
170
+ parts.append(f"Effectiveness {rate:.0%} ({pos} positive, {neg} negative of {outputs} outputs)")
171
+
172
+ # Pattern highlights
173
+ high = feedback_scores.get("high", [])
174
+ low = feedback_scores.get("low", [])
175
+ if high:
176
+ parts.append(f"Working well: {', '.join(high[:3])}")
177
+ if low:
178
+ parts.append(f"Underperforming: {', '.join(low[:3])}")
179
+
180
+ # Directive rationale
181
+ rationale = {
182
+ "aggressive_prune": "Low effectiveness — pruning weak patterns",
183
+ "normal": "Balanced cycle — standard add/prune",
184
+ "stability": "High effectiveness — preserving current patterns",
185
+ "insufficient_data": "Not enough data for effectiveness tuning",
186
+ }
187
+ parts.append(rationale.get(directive, f"Directive: {directive}"))
188
+
189
+ return ". ".join(parts)
190
+
191
+
192
+ def main():
193
+ parser = argparse.ArgumentParser(description="Phase 3: Feedback analysis")
194
+ parser.add_argument("--memory-dir", required=True, help="Path to memory/ directory")
195
+ parser.add_argument("--session-summary", required=True, help="Brief session summary")
196
+ args = parser.parse_args()
197
+
198
+ logs = read_recent_logs(args.memory_dir, days=7)
199
+
200
+ # Fully mechanical computation — no LLM call
201
+ effectiveness = compute_effectiveness(logs)
202
+ feedback_scores = extract_feedback_scores(logs)
203
+ directive = determine_directive(effectiveness)
204
+ interpretation = generate_interpretation(feedback_scores, effectiveness, directive, logs)
205
+
206
+ # Compute skip rate for proactivity tracking
207
+ total_ticks = len(logs)
208
+ skip_ticks = sum(1 for e in logs if e.get("skipped", True))
209
+ skip_rate = round(skip_ticks / total_ticks, 2) if total_ticks > 0 else 1.0
210
+
211
+ output_json({
212
+ "feedbackScores": feedback_scores,
213
+ "effectiveness": effectiveness,
214
+ "curateDirective": directive,
215
+ "interpretation": interpretation,
216
+ "skipRate": skip_rate,
217
+ })
218
+
219
+
220
+ if __name__ == "__main__":
221
+ main()
@@ -0,0 +1,19 @@
1
+ #!/usr/bin/env bash
2
+ # Phase 1: Git backup — commit and push any uncommitted changes in the workspace.
3
+ # Runs from the workspace root. Exits 0 on success or nothing to commit, 1 on push failure.
4
+
5
+ set -euo pipefail
6
+
7
+ changes=$(git status --porcelain 2>/dev/null || true)
8
+
9
+ if [ -z "$changes" ]; then
10
+ echo "nothing to commit"
11
+ exit 0
12
+ fi
13
+
14
+ git add -A
15
+ git commit -m "auto: heartbeat $(date -u +%Y-%m-%dT%H:%M:%SZ)"
16
+ git push origin main
17
+
18
+ # Output the commit hash
19
+ git rev-parse --short HEAD
@@ -0,0 +1,181 @@
1
+ #!/usr/bin/env python3
2
+ """Phase 3 Step 5: Insight Synthesizer — produce suggestion + insight for Telegram.
3
+
4
+ Uses the "smart" model (configured in koog-config.json) for higher quality output.
5
+ Reads post-curation playbook + recent logs to generate one Telegram message
6
+ with a practical suggestion and a surprising insight.
7
+
8
+ Usage:
9
+ python3 insight_synthesizer.py --memory-dir memory/ --session-summary "..." \
10
+ [--curator-changes "TEXT"] [--idle]
11
+ """
12
+
13
+ import argparse
14
+ import json
15
+ import sys
16
+
17
+ from common import (
18
+ LLMError,
19
+ call_llm,
20
+ extract_json,
21
+ output_json,
22
+ read_effective_playbook,
23
+ read_recent_logs,
24
+ )
25
+
26
+ SYSTEM_PROMPT = """\
27
+ You are the insight synthesizer for sinain, a personal AI assistant.
28
+ Your job: produce ONE high-quality Telegram message with two parts.
29
+
30
+ **Suggestion** (1-2 sentences): A practical, actionable recommendation.
31
+ - MUST reference a specific playbook pattern or concrete observation
32
+ - NOT generic advice — grounded in actual data
33
+ - Could be: workflow improvement, recurring problem to automate, successful pattern to replicate
34
+
35
+ **Insight** (1-2 sentences): A surprising, non-obvious connection from accumulated data.
36
+ - MUST connect 2+ distinct observations that aren't obviously related
37
+ - Cross-domain patterns, unexpected correlations, things the user hasn't noticed
38
+ - Cite specific observations from playbook or logs
39
+
40
+ Quality gate — you MUST skip if:
41
+ - You cannot produce BOTH a genuinely useful suggestion AND a genuinely surprising insight
42
+ - The suggestion would repeat something from recent heartbeat outputs
43
+ - The insight is obvious or doesn't connect distinct observations
44
+
45
+ Proactivity rules:
46
+ - On ACTIVE days, LOWER your skip threshold — the user wants daily engagement
47
+ - If the last 3+ outputs were skipped, you MUST produce output (find something worth saying)
48
+ - Prefer a moderate-quality insight over silence — the user explicitly asked for more proactive insights
49
+ - When idle, focus on mined pattern synthesis and cross-day trend observations
50
+
51
+ Total message MUST be under 500 characters.
52
+
53
+ Respond with ONLY a JSON object. If producing output:
54
+ {
55
+ "skip": false,
56
+ "suggestion": "the suggestion text",
57
+ "insight": "the insight text"
58
+ }
59
+
60
+ If skipping (be specific about WHY, citing what you read):
61
+ {
62
+ "skip": true,
63
+ "skipReason": "specific reason citing files/patterns examined — 'no new data' is NOT valid"
64
+ }
65
+
66
+ When your suggestion or insight references a pattern from a section marked
67
+ "[Transferred knowledge: ...]", naturally mention this origin (e.g. "Based on
68
+ transferred OCR expertise, ..."). Keep the attribution brief and natural."""
69
+
70
+
71
+ def build_user_prompt(
72
+ playbook: str,
73
+ recent_logs: list[dict],
74
+ session_summary: str,
75
+ curator_changes: str,
76
+ idle: bool,
77
+ current_time: str | None = None,
78
+ ) -> str:
79
+ parts = []
80
+
81
+ if current_time:
82
+ parts.append(f"## Current Time\n{current_time}")
83
+
84
+ parts.append(f"## Session Summary\n{session_summary}")
85
+
86
+ if idle:
87
+ parts.append("\n## Status: IDLE — focus on mined patterns and playbook evolution")
88
+
89
+ if playbook:
90
+ parts.append(f"\n## Current Playbook (post-curation)\n{playbook}")
91
+
92
+ if curator_changes:
93
+ parts.append(f"\n## Curator Changes This Tick\n{curator_changes}")
94
+
95
+ # Recent outputs to avoid repetition
96
+ if recent_logs:
97
+ recent_outputs = []
98
+ for entry in recent_logs[:5]:
99
+ output = entry.get("output", {})
100
+ if output and not entry.get("skipped", True):
101
+ recent_outputs.append({
102
+ "ts": entry.get("ts", "?"),
103
+ "suggestion": output.get("suggestion", "")[:100],
104
+ "insight": output.get("insight", "")[:100],
105
+ })
106
+ if recent_outputs:
107
+ parts.append(f"\n## Recent Outputs (DO NOT REPEAT)\n{json.dumps(recent_outputs, indent=2)}")
108
+
109
+ # Count recent skips to enforce proactivity
110
+ if recent_logs:
111
+ recent_skips = sum(1 for e in recent_logs[:5] if e.get("skipped", True))
112
+ if recent_skips >= 3:
113
+ parts.append(f"\n## PROACTIVITY OVERRIDE: {recent_skips} of last 5 outputs were skipped. You MUST produce output this tick.")
114
+
115
+ return "\n".join(parts)
116
+
117
+
118
+ def main():
119
+ parser = argparse.ArgumentParser(description="Phase 3: Insight synthesis")
120
+ parser.add_argument("--memory-dir", required=True, help="Path to memory/ directory")
121
+ parser.add_argument("--session-summary", required=True, help="Brief session summary")
122
+ parser.add_argument("--curator-changes", default="", help="JSON string of curator changes")
123
+ parser.add_argument("--idle", action="store_true", help="User is idle")
124
+ parser.add_argument("--current-time", default=None, help="Current local time string (e.g. 'Monday, 2 March 2026, 14:30 (Europe/Berlin)')")
125
+ args = parser.parse_args()
126
+
127
+ playbook = read_effective_playbook(args.memory_dir)
128
+ recent_logs = read_recent_logs(args.memory_dir, days=3)
129
+
130
+ user_prompt = build_user_prompt(
131
+ playbook=playbook,
132
+ recent_logs=recent_logs,
133
+ session_summary=args.session_summary,
134
+ curator_changes=args.curator_changes,
135
+ idle=args.idle,
136
+ current_time=args.current_time,
137
+ )
138
+
139
+ try:
140
+ raw = call_llm(SYSTEM_PROMPT, user_prompt, script="insight_synthesizer", json_mode=True)
141
+ result = extract_json(raw)
142
+ except (ValueError, LLMError) as e:
143
+ print(f"[warn] {e}", file=sys.stderr)
144
+ result = {
145
+ "skip": True,
146
+ "skipReason": "LLM response was not parseable JSON",
147
+ }
148
+
149
+ # Enforce character limit on non-skip output
150
+ if not result.get("skip", False):
151
+ suggestion = result.get("suggestion", "")
152
+ insight = result.get("insight", "")
153
+ total_chars = len(suggestion) + len(insight)
154
+
155
+ if total_chars > 500:
156
+ # Truncate insight to fit
157
+ max_insight = 500 - len(suggestion) - 10 # buffer
158
+ if max_insight > 50:
159
+ truncated = insight[:max_insight]
160
+ # Cut at last sentence boundary
161
+ last_period = truncated.rfind(".")
162
+ last_excl = truncated.rfind("!")
163
+ last_q = truncated.rfind("?")
164
+ boundary = max(last_period, last_excl, last_q)
165
+ if boundary > max_insight // 2:
166
+ insight = truncated[:boundary + 1]
167
+ else:
168
+ insight = truncated.rstrip() + "..."
169
+ result["insight"] = insight
170
+ total_chars = len(suggestion) + len(insight)
171
+ else:
172
+ result["skip"] = True
173
+ result["skipReason"] = f"Output exceeded 500 chars ({total_chars}) and could not be trimmed"
174
+
175
+ result["totalChars"] = len(result.get("suggestion", "")) + len(result.get("insight", ""))
176
+
177
+ output_json(result)
178
+
179
+
180
+ if __name__ == "__main__":
181
+ main()
@@ -0,0 +1,11 @@
1
+ # 2026-03-01 Session Notes
2
+
3
+ ## OCR Pipeline
4
+ - Switched from Tesseract to OpenRouter vision API
5
+ - Backpressure issue: camera produces frames faster than API can process
6
+ - Solution: frame dropping with scene-gate (skip similar consecutive frames)
7
+
8
+ ## Wearable HUD
9
+ - Testing 3-panel debug interface
10
+ - Camera feed, OCR overlay, and pipeline stats side-by-side
11
+ - Found that JPEG quality 70 is good balance of speed vs readability
@@ -0,0 +1,15 @@
1
+ <!-- mining-index: 2026-03-01 -->
2
+ # Sinain Playbook
3
+
4
+ ## Established Patterns
5
+ - When OCR pipeline stalls, check camera frame queue depth (score: 0.8)
6
+ - When user explores new framework, spawn research agent proactively (score: 0.6)
7
+
8
+ ## Observed
9
+ - User prefers concise Telegram messages over detailed ones
10
+ - Late evening sessions tend to be exploratory/research-heavy
11
+
12
+ ## Stale
13
+ - Flutter overlay rendering glitch on macOS 15 [since: 2026-02-18]
14
+
15
+ <!-- effectiveness: outputs=8, positive=5, negative=1, neutral=2, rate=0.63, updated=2026-02-21 -->
@@ -0,0 +1 @@
1
+ {"ts": "2026-03-01T14:18:16.183587+00:00", "idle": false, "sessionHistorySummary": "User debugging OCR pipeline", "feedbackScores": {"avg": 0.35, "high": ["OCR fix suggestion"], "low": []}, "actionsConsidered": [{"action": "spawn research", "reason": "Flutter overlay perf", "chosen": false, "skipReason": "not urgent"}], "effectivenessRate": 0.63, "effectivenessAlert": false, "playbookChanges": {"added": [], "pruned": [], "promoted": []}, "output": {"suggestion": "Consider frame batching for OCR pipeline", "insight": "Evening sessions correlate with exploratory work"}, "skipped": false}
@@ -0,0 +1,21 @@
1
+ <!-- mining-index: 2026-03-01 -->
2
+
3
+ # Sinain Playbook
4
+
5
+ ## Established Patterns
6
+ - When OCR pipeline stalls, check camera frame queue depth (score: 0.8)
7
+ - When user explores new framework, spawn research agent proactively (score: 0.6)
8
+ - When OCR backend switches (e.g., Tesseract → vision API), validate latency and cost tradeoffs before committing (score: 0.7)
9
+
10
+ ## Observed
11
+ - User prefers concise Telegram messages over detailed ones
12
+ - Late evening sessions tend to be exploratory/research-heavy
13
+ - Scene-gating (drop frames on low scene change) is preferred approach for OCR backpressure
14
+ - JPEG quality tuning is part of wearable pipeline optimization workflow
15
+ - User converging on 3-panel HUD layout for wearable debug interface
16
+ - Flutter overlay on macOS is an active exploration area (not settled)
17
+
18
+ ## Stale
19
+ - Flutter overlay rendering glitch on macOS 15 [since: 2026-02-18]
20
+
21
+ <!-- effectiveness: outputs=8, positive=5, negative=1, neutral=2, rate=0.63, updated=2026-02-21 -->
@@ -0,0 +1,39 @@
1
+ {
2
+ "models": {
3
+ "fast": "google/gemini-3-flash-preview",
4
+ "smart": "anthropic/claude-sonnet-4.6"
5
+ },
6
+ "scripts": {
7
+ "signal_analyzer": { "model": "fast", "maxTokens": 1500 },
8
+ "feedback_analyzer": { "model": "fast", "maxTokens": 800 },
9
+ "memory_miner": { "model": "smart", "maxTokens": 1500 },
10
+ "playbook_curator": { "model": "fast", "maxTokens": 3000, "timeout": 90 },
11
+ "insight_synthesizer": { "model": "smart", "maxTokens": 800 },
12
+ "module_manager": { "model": "fast", "maxTokens": 2000 },
13
+ "tick_evaluator": { "model": "smart", "maxTokens": 200, "timeout": 30 },
14
+ "eval_reporter": { "model": "smart", "maxTokens": 1000 },
15
+ "triple_extractor": { "model": "fast", "maxTokens": 1500, "timeout": 30 }
16
+ },
17
+ "defaults": { "model": "fast", "maxTokens": 1500 },
18
+ "triplestore": {
19
+ "dbPath": "memory/triplestore.db",
20
+ "maxTriplesPerTx": 100,
21
+ "conceptExtractionMode": "tiered",
22
+ "gcOlderThanDays": 30
23
+ },
24
+ "eval": {
25
+ "level": "sampled",
26
+ "sampleRate": 0.3,
27
+ "judges": {
28
+ "model": "smart",
29
+ "maxTokens": 200,
30
+ "timeout": 30
31
+ },
32
+ "dailyReport": true,
33
+ "regressionThresholds": {
34
+ "assertionPassRate": 0.85,
35
+ "effectivenessRate": 0.4,
36
+ "skipRate": 0.8
37
+ }
38
+ }
39
+ }