nemoris 0.1.0 → 0.1.2
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.
- package/.env.example +49 -49
- package/LICENSE +21 -21
- package/README.md +209 -209
- package/SECURITY.md +59 -119
- package/bin/nemoris +46 -46
- package/config/agents/agent.toml.example +28 -28
- package/config/agents/content.toml +23 -0
- package/config/agents/default.toml +22 -22
- package/config/agents/heartbeat.toml +35 -0
- package/config/agents/iris.toml +23 -0
- package/config/agents/lab.toml +23 -0
- package/config/agents/main.toml +45 -0
- package/config/agents/nemo.toml +21 -0
- package/config/agents/ops.toml +38 -0
- package/config/agents/orchestrator.toml +18 -18
- package/config/agents/revenue.toml +23 -0
- package/config/agents/testyboo.toml +19 -0
- package/config/delivery.toml +73 -73
- package/config/embeddings.toml +5 -5
- package/config/identity/content-purpose.md +11 -0
- package/config/identity/content-soul.md +45 -0
- package/config/identity/default-purpose.md +1 -1
- package/config/identity/default-soul.md +3 -3
- package/config/identity/heartbeat-purpose.md +9 -0
- package/config/identity/heartbeat-soul.md +16 -0
- package/config/identity/iris-purpose.md +17 -0
- package/config/identity/iris-soul.md +68 -0
- package/config/identity/lab-purpose.md +10 -0
- package/config/identity/lab-soul.md +38 -0
- package/config/identity/main-purpose.md +17 -0
- package/config/identity/main-soul.md +66 -0
- package/config/identity/main-user.md +22 -0
- package/config/identity/ops-purpose.md +9 -0
- package/config/identity/ops-soul.md +16 -0
- package/config/identity/orchestrator-purpose.md +1 -1
- package/config/identity/orchestrator-soul.md +1 -1
- package/config/identity/revenue-purpose.md +9 -0
- package/config/identity/revenue-soul.md +41 -0
- package/config/identity/testyboo-purpose.md +13 -0
- package/config/identity/testyboo-soul.md +20 -0
- package/config/improvement-targets.toml +15 -15
- package/config/jobs/heartbeat-check.toml +30 -30
- package/config/jobs/memory-rollup.toml +46 -46
- package/config/jobs/workspace-health.toml +63 -63
- package/config/mcp.toml +16 -16
- package/config/output-contracts.toml +17 -17
- package/config/peers.toml +32 -32
- package/config/peers.toml.example +32 -32
- package/config/policies/memory-default.toml +10 -10
- package/config/policies/memory-heartbeat.toml +5 -5
- package/config/policies/memory-ops.toml +10 -10
- package/config/policies/tools-heartbeat-minimal.toml +8 -8
- package/config/policies/tools-interactive-safe.toml +8 -8
- package/config/policies/tools-ops-bounded.toml +8 -8
- package/config/policies/tools-orchestrator.toml +7 -7
- package/config/providers/anthropic.toml +15 -15
- package/config/providers/ollama.toml +5 -5
- package/config/providers/openai-codex.toml +9 -9
- package/config/providers/openrouter.toml +5 -5
- package/config/router.toml +22 -22
- package/config/runtime.toml +114 -114
- package/config/skills/self-improvement.toml +15 -15
- package/config/skills/telegram-onboarding-spec.md +240 -240
- package/config/skills/workspace-monitor.toml +15 -15
- package/config/task-router.toml +42 -42
- package/install.sh +50 -50
- package/package.json +91 -90
- package/src/auth/auth-profiles.js +169 -169
- package/src/auth/openai-codex-oauth.js +285 -285
- package/src/battle.js +449 -449
- package/src/cli/help.js +265 -265
- package/src/cli/output-filter.js +49 -49
- package/src/cli/runtime-control.js +704 -704
- package/src/cli-main.js +2763 -2763
- package/src/cli.js +78 -78
- package/src/config/loader.js +332 -332
- package/src/config/schema-validator.js +214 -214
- package/src/config/toml-lite.js +8 -8
- package/src/daemon/action-handlers.js +71 -71
- package/src/daemon/healing-tick.js +87 -87
- package/src/daemon/health-probes.js +90 -90
- package/src/daemon/notifier.js +57 -57
- package/src/daemon/nurse.js +218 -218
- package/src/daemon/repair-log.js +106 -106
- package/src/daemon/rule-staging.js +90 -90
- package/src/daemon/rules.js +29 -29
- package/src/daemon/telegram-commands.js +54 -54
- package/src/daemon/updater.js +85 -85
- package/src/jobs/job-runner.js +78 -78
- package/src/mcp/consumer.js +129 -129
- package/src/memory/active-recall.js +171 -171
- package/src/memory/backend-manager.js +97 -97
- package/src/memory/backends/file-backend.js +38 -38
- package/src/memory/backends/qmd-backend.js +219 -219
- package/src/memory/embedding-guards.js +24 -24
- package/src/memory/embedding-index.js +118 -118
- package/src/memory/embedding-service.js +179 -179
- package/src/memory/file-index.js +177 -177
- package/src/memory/memory-signature.js +5 -5
- package/src/memory/memory-store.js +648 -648
- package/src/memory/retrieval-planner.js +66 -66
- package/src/memory/scoring.js +145 -145
- package/src/memory/simhash.js +78 -78
- package/src/memory/sqlite-active-store.js +824 -824
- package/src/memory/write-policy.js +36 -36
- package/src/onboarding/aliases.js +33 -33
- package/src/onboarding/auth/api-key.js +224 -224
- package/src/onboarding/auth/ollama-detect.js +42 -42
- package/src/onboarding/clack-prompter.js +77 -77
- package/src/onboarding/doctor.js +530 -530
- package/src/onboarding/lock.js +42 -42
- package/src/onboarding/model-catalog.js +344 -344
- package/src/onboarding/phases/auth.js +576 -589
- package/src/onboarding/phases/build.js +130 -130
- package/src/onboarding/phases/choose.js +82 -82
- package/src/onboarding/phases/detect.js +98 -98
- package/src/onboarding/phases/hatch.js +216 -216
- package/src/onboarding/phases/identity.js +79 -79
- package/src/onboarding/phases/ollama.js +345 -345
- package/src/onboarding/phases/scaffold.js +99 -99
- package/src/onboarding/phases/telegram.js +377 -377
- package/src/onboarding/phases/validate.js +204 -204
- package/src/onboarding/phases/verify.js +206 -206
- package/src/onboarding/platform.js +482 -482
- package/src/onboarding/status-bar.js +95 -95
- package/src/onboarding/templates.js +794 -794
- package/src/onboarding/toml-writer.js +38 -38
- package/src/onboarding/tui.js +250 -250
- package/src/onboarding/uninstall.js +153 -153
- package/src/onboarding/wizard.js +516 -499
- package/src/providers/anthropic.js +168 -168
- package/src/providers/base.js +247 -247
- package/src/providers/circuit-breaker.js +136 -136
- package/src/providers/ollama.js +163 -163
- package/src/providers/openai-codex.js +149 -149
- package/src/providers/openrouter.js +136 -136
- package/src/providers/registry.js +36 -36
- package/src/providers/router.js +16 -16
- package/src/runtime/bootstrap-cache.js +47 -47
- package/src/runtime/capabilities-prompt.js +25 -25
- package/src/runtime/completion-ping.js +99 -99
- package/src/runtime/config-validator.js +121 -121
- package/src/runtime/context-ledger.js +360 -360
- package/src/runtime/cutover-readiness.js +42 -42
- package/src/runtime/daemon.js +729 -729
- package/src/runtime/delivery-ack.js +195 -195
- package/src/runtime/delivery-adapters/local-file.js +41 -41
- package/src/runtime/delivery-adapters/openclaw-cli.js +94 -94
- package/src/runtime/delivery-adapters/openclaw-peer.js +98 -98
- package/src/runtime/delivery-adapters/shadow.js +13 -13
- package/src/runtime/delivery-adapters/standalone-http.js +98 -98
- package/src/runtime/delivery-adapters/telegram.js +104 -104
- package/src/runtime/delivery-adapters/tui.js +128 -128
- package/src/runtime/delivery-manager.js +807 -807
- package/src/runtime/delivery-store.js +168 -168
- package/src/runtime/dependency-health.js +118 -118
- package/src/runtime/envelope.js +114 -114
- package/src/runtime/evaluation.js +1089 -1089
- package/src/runtime/exec-approvals.js +216 -216
- package/src/runtime/executor.js +500 -500
- package/src/runtime/failure-ping.js +67 -67
- package/src/runtime/flows.js +83 -83
- package/src/runtime/guards.js +45 -45
- package/src/runtime/handoff.js +51 -51
- package/src/runtime/identity-cache.js +28 -28
- package/src/runtime/improvement-engine.js +109 -109
- package/src/runtime/improvement-harness.js +581 -581
- package/src/runtime/input-sanitiser.js +72 -72
- package/src/runtime/interaction-contract.js +347 -347
- package/src/runtime/lane-readiness.js +226 -226
- package/src/runtime/migration.js +323 -323
- package/src/runtime/model-resolution.js +78 -78
- package/src/runtime/network.js +64 -64
- package/src/runtime/notification-store.js +97 -97
- package/src/runtime/notifier.js +256 -256
- package/src/runtime/orchestrator.js +53 -53
- package/src/runtime/orphan-reaper.js +41 -41
- package/src/runtime/output-contract-schema.js +139 -139
- package/src/runtime/output-contract-validator.js +439 -439
- package/src/runtime/peer-readiness.js +69 -69
- package/src/runtime/peer-registry.js +133 -133
- package/src/runtime/pilot-status.js +108 -108
- package/src/runtime/prompt-builder.js +261 -261
- package/src/runtime/provider-attempt.js +582 -582
- package/src/runtime/report-fallback.js +71 -71
- package/src/runtime/result-normalizer.js +183 -183
- package/src/runtime/retention.js +74 -74
- package/src/runtime/review.js +244 -244
- package/src/runtime/route-job.js +15 -15
- package/src/runtime/run-store.js +38 -38
- package/src/runtime/schedule.js +88 -88
- package/src/runtime/scheduler-state.js +434 -434
- package/src/runtime/scheduler.js +656 -656
- package/src/runtime/session-compactor.js +182 -182
- package/src/runtime/session-search.js +155 -155
- package/src/runtime/slack-inbound.js +249 -249
- package/src/runtime/ssrf.js +102 -102
- package/src/runtime/status-aggregator.js +330 -330
- package/src/runtime/task-contract.js +140 -140
- package/src/runtime/task-packet.js +107 -107
- package/src/runtime/task-router.js +140 -140
- package/src/runtime/telegram-inbound.js +1565 -1565
- package/src/runtime/token-counter.js +134 -134
- package/src/runtime/token-estimator.js +59 -59
- package/src/runtime/tool-loop.js +200 -200
- package/src/runtime/transport-server.js +311 -311
- package/src/runtime/tui-server.js +411 -411
- package/src/runtime/ulid.js +44 -44
- package/src/security/ssrf-check.js +197 -197
- package/src/setup.js +369 -369
- package/src/shadow/bridge.js +303 -303
- package/src/skills/loader.js +84 -84
- package/src/tools/catalog.json +49 -49
- package/src/tools/cli-delegate.js +44 -44
- package/src/tools/mcp-client.js +106 -106
- package/src/tools/micro/cancel-task.js +6 -6
- package/src/tools/micro/complete-task.js +6 -6
- package/src/tools/micro/fail-task.js +6 -6
- package/src/tools/micro/http-fetch.js +74 -74
- package/src/tools/micro/index.js +36 -36
- package/src/tools/micro/lcm-recall.js +60 -60
- package/src/tools/micro/list-dir.js +17 -17
- package/src/tools/micro/list-skills.js +46 -46
- package/src/tools/micro/load-skill.js +38 -38
- package/src/tools/micro/memory-search.js +45 -45
- package/src/tools/micro/read-file.js +11 -11
- package/src/tools/micro/session-search.js +54 -54
- package/src/tools/micro/shell-exec.js +43 -43
- package/src/tools/micro/trigger-job.js +79 -79
- package/src/tools/micro/web-search.js +58 -58
- package/src/tools/micro/workspace-paths.js +39 -39
- package/src/tools/micro/write-file.js +14 -14
- package/src/tools/micro/write-memory.js +41 -41
- package/src/tools/registry.js +348 -348
- package/src/tools/tool-result-contract.js +36 -36
- package/src/tui/chat.js +835 -835
- package/src/tui/renderer.js +175 -175
- package/src/tui/socket-client.js +217 -217
- package/src/utils/canonical-json.js +29 -29
- package/src/utils/compaction.js +30 -30
- package/src/utils/env-loader.js +5 -5
- package/src/utils/errors.js +80 -80
- package/src/utils/fs.js +101 -101
- package/src/utils/ids.js +5 -5
- package/src/utils/model-context-limits.js +30 -30
- package/src/utils/token-budget.js +74 -74
- package/src/utils/usage-cost.js +25 -25
- package/src/utils/usage-metrics.js +14 -14
|
@@ -1,134 +1,134 @@
|
|
|
1
|
-
import { estimateInvocationTokens } from "./token-estimator.js";
|
|
2
|
-
|
|
3
|
-
let jsTiktokenModulePromise = null;
|
|
4
|
-
|
|
5
|
-
function normalizeModelId(modelId) {
|
|
6
|
-
return String(modelId || "").toLowerCase();
|
|
7
|
-
}
|
|
8
|
-
|
|
9
|
-
function extractOpenAiTokenizerModel(modelId) {
|
|
10
|
-
const normalized = normalizeModelId(modelId);
|
|
11
|
-
const candidates = [
|
|
12
|
-
"gpt-4.1-mini",
|
|
13
|
-
"gpt-4.1",
|
|
14
|
-
"gpt-4o-mini",
|
|
15
|
-
"gpt-4o",
|
|
16
|
-
"o3-mini",
|
|
17
|
-
"o3",
|
|
18
|
-
"o1-mini",
|
|
19
|
-
"o1",
|
|
20
|
-
"gpt-4-turbo",
|
|
21
|
-
"gpt-4",
|
|
22
|
-
"gpt-3.5-turbo"
|
|
23
|
-
];
|
|
24
|
-
|
|
25
|
-
for (const candidate of candidates) {
|
|
26
|
-
if (normalized.includes(candidate)) {
|
|
27
|
-
return candidate;
|
|
28
|
-
}
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
return null;
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
function renderInvocationText(invocation) {
|
|
35
|
-
const sections = [];
|
|
36
|
-
if (invocation?.system) {
|
|
37
|
-
sections.push(`system\n${invocation.system}`);
|
|
38
|
-
}
|
|
39
|
-
for (const message of invocation?.messages || []) {
|
|
40
|
-
sections.push(`${message.role || "user"}\n${message.content || ""}`);
|
|
41
|
-
}
|
|
42
|
-
return sections.join("\n\n");
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
async function loadJsTiktoken() {
|
|
46
|
-
if (!jsTiktokenModulePromise) {
|
|
47
|
-
jsTiktokenModulePromise = import("js-tiktoken").catch(() => null);
|
|
48
|
-
}
|
|
49
|
-
return jsTiktokenModulePromise;
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
function countWithApproximateTokenizer(modelName, invocation) {
|
|
53
|
-
const rendered = renderInvocationText(invocation);
|
|
54
|
-
const textTokens = Math.max(1, Math.ceil(rendered.length / 4));
|
|
55
|
-
const framing = invocation?.messages?.length ? invocation.messages.length * 6 : 0;
|
|
56
|
-
return {
|
|
57
|
-
total: textTokens + framing,
|
|
58
|
-
mode: "model_specific",
|
|
59
|
-
source: `js-tiktoken:${modelName}`,
|
|
60
|
-
details: {
|
|
61
|
-
textTokens,
|
|
62
|
-
framing,
|
|
63
|
-
fallback: "approximate_model_specific",
|
|
64
|
-
}
|
|
65
|
-
};
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
async function countWithJsTiktoken(modelName, invocation) {
|
|
69
|
-
const jsTiktoken = await loadJsTiktoken();
|
|
70
|
-
if (!jsTiktoken?.encodingForModel) {
|
|
71
|
-
return countWithApproximateTokenizer(modelName, invocation);
|
|
72
|
-
}
|
|
73
|
-
const encoder = jsTiktoken.encodingForModel(modelName);
|
|
74
|
-
const rendered = renderInvocationText(invocation);
|
|
75
|
-
const textTokens = encoder.encode(rendered).length;
|
|
76
|
-
const framing = invocation?.messages?.length ? invocation.messages.length * 6 : 0;
|
|
77
|
-
return {
|
|
78
|
-
total: textTokens + framing,
|
|
79
|
-
mode: "model_specific",
|
|
80
|
-
source: `js-tiktoken:${modelName}`,
|
|
81
|
-
details: {
|
|
82
|
-
textTokens,
|
|
83
|
-
framing
|
|
84
|
-
}
|
|
85
|
-
};
|
|
86
|
-
}
|
|
87
|
-
|
|
88
|
-
export class TokenCounter {
|
|
89
|
-
async countInvocation({ providerId, invocation, adapter }) {
|
|
90
|
-
if (adapter && typeof adapter.countTokens === "function") {
|
|
91
|
-
try {
|
|
92
|
-
const total = await adapter.countTokens(invocation);
|
|
93
|
-
if (Number.isFinite(total) && total > 0) {
|
|
94
|
-
return {
|
|
95
|
-
total,
|
|
96
|
-
mode: "exact",
|
|
97
|
-
source: `${providerId}:count_tokens`,
|
|
98
|
-
details: {
|
|
99
|
-
providerId
|
|
100
|
-
}
|
|
101
|
-
};
|
|
102
|
-
}
|
|
103
|
-
} catch (error) {
|
|
104
|
-
const fallback = this.countHeuristic(invocation);
|
|
105
|
-
return {
|
|
106
|
-
...fallback,
|
|
107
|
-
details: {
|
|
108
|
-
...fallback.details,
|
|
109
|
-
fallbackReason: error.message
|
|
110
|
-
}
|
|
111
|
-
};
|
|
112
|
-
}
|
|
113
|
-
}
|
|
114
|
-
|
|
115
|
-
const tokenizerModel = extractOpenAiTokenizerModel(invocation?.model);
|
|
116
|
-
if (tokenizerModel) {
|
|
117
|
-
try {
|
|
118
|
-
return await countWithJsTiktoken(tokenizerModel, invocation);
|
|
119
|
-
} catch {}
|
|
120
|
-
}
|
|
121
|
-
|
|
122
|
-
return this.countHeuristic(invocation);
|
|
123
|
-
}
|
|
124
|
-
|
|
125
|
-
countHeuristic(invocation) {
|
|
126
|
-
const estimate = estimateInvocationTokens(invocation);
|
|
127
|
-
return {
|
|
128
|
-
total: estimate.total,
|
|
129
|
-
mode: "heuristic",
|
|
130
|
-
source: "multi_strategy_estimator",
|
|
131
|
-
details: estimate
|
|
132
|
-
};
|
|
133
|
-
}
|
|
134
|
-
}
|
|
1
|
+
import { estimateInvocationTokens } from "./token-estimator.js";
|
|
2
|
+
|
|
3
|
+
let jsTiktokenModulePromise = null;
|
|
4
|
+
|
|
5
|
+
function normalizeModelId(modelId) {
|
|
6
|
+
return String(modelId || "").toLowerCase();
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
function extractOpenAiTokenizerModel(modelId) {
|
|
10
|
+
const normalized = normalizeModelId(modelId);
|
|
11
|
+
const candidates = [
|
|
12
|
+
"gpt-4.1-mini",
|
|
13
|
+
"gpt-4.1",
|
|
14
|
+
"gpt-4o-mini",
|
|
15
|
+
"gpt-4o",
|
|
16
|
+
"o3-mini",
|
|
17
|
+
"o3",
|
|
18
|
+
"o1-mini",
|
|
19
|
+
"o1",
|
|
20
|
+
"gpt-4-turbo",
|
|
21
|
+
"gpt-4",
|
|
22
|
+
"gpt-3.5-turbo"
|
|
23
|
+
];
|
|
24
|
+
|
|
25
|
+
for (const candidate of candidates) {
|
|
26
|
+
if (normalized.includes(candidate)) {
|
|
27
|
+
return candidate;
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
return null;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
function renderInvocationText(invocation) {
|
|
35
|
+
const sections = [];
|
|
36
|
+
if (invocation?.system) {
|
|
37
|
+
sections.push(`system\n${invocation.system}`);
|
|
38
|
+
}
|
|
39
|
+
for (const message of invocation?.messages || []) {
|
|
40
|
+
sections.push(`${message.role || "user"}\n${message.content || ""}`);
|
|
41
|
+
}
|
|
42
|
+
return sections.join("\n\n");
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
async function loadJsTiktoken() {
|
|
46
|
+
if (!jsTiktokenModulePromise) {
|
|
47
|
+
jsTiktokenModulePromise = import("js-tiktoken").catch(() => null);
|
|
48
|
+
}
|
|
49
|
+
return jsTiktokenModulePromise;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
function countWithApproximateTokenizer(modelName, invocation) {
|
|
53
|
+
const rendered = renderInvocationText(invocation);
|
|
54
|
+
const textTokens = Math.max(1, Math.ceil(rendered.length / 4));
|
|
55
|
+
const framing = invocation?.messages?.length ? invocation.messages.length * 6 : 0;
|
|
56
|
+
return {
|
|
57
|
+
total: textTokens + framing,
|
|
58
|
+
mode: "model_specific",
|
|
59
|
+
source: `js-tiktoken:${modelName}`,
|
|
60
|
+
details: {
|
|
61
|
+
textTokens,
|
|
62
|
+
framing,
|
|
63
|
+
fallback: "approximate_model_specific",
|
|
64
|
+
}
|
|
65
|
+
};
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
async function countWithJsTiktoken(modelName, invocation) {
|
|
69
|
+
const jsTiktoken = await loadJsTiktoken();
|
|
70
|
+
if (!jsTiktoken?.encodingForModel) {
|
|
71
|
+
return countWithApproximateTokenizer(modelName, invocation);
|
|
72
|
+
}
|
|
73
|
+
const encoder = jsTiktoken.encodingForModel(modelName);
|
|
74
|
+
const rendered = renderInvocationText(invocation);
|
|
75
|
+
const textTokens = encoder.encode(rendered).length;
|
|
76
|
+
const framing = invocation?.messages?.length ? invocation.messages.length * 6 : 0;
|
|
77
|
+
return {
|
|
78
|
+
total: textTokens + framing,
|
|
79
|
+
mode: "model_specific",
|
|
80
|
+
source: `js-tiktoken:${modelName}`,
|
|
81
|
+
details: {
|
|
82
|
+
textTokens,
|
|
83
|
+
framing
|
|
84
|
+
}
|
|
85
|
+
};
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
export class TokenCounter {
|
|
89
|
+
async countInvocation({ providerId, invocation, adapter }) {
|
|
90
|
+
if (adapter && typeof adapter.countTokens === "function") {
|
|
91
|
+
try {
|
|
92
|
+
const total = await adapter.countTokens(invocation);
|
|
93
|
+
if (Number.isFinite(total) && total > 0) {
|
|
94
|
+
return {
|
|
95
|
+
total,
|
|
96
|
+
mode: "exact",
|
|
97
|
+
source: `${providerId}:count_tokens`,
|
|
98
|
+
details: {
|
|
99
|
+
providerId
|
|
100
|
+
}
|
|
101
|
+
};
|
|
102
|
+
}
|
|
103
|
+
} catch (error) {
|
|
104
|
+
const fallback = this.countHeuristic(invocation);
|
|
105
|
+
return {
|
|
106
|
+
...fallback,
|
|
107
|
+
details: {
|
|
108
|
+
...fallback.details,
|
|
109
|
+
fallbackReason: error.message
|
|
110
|
+
}
|
|
111
|
+
};
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
const tokenizerModel = extractOpenAiTokenizerModel(invocation?.model);
|
|
116
|
+
if (tokenizerModel) {
|
|
117
|
+
try {
|
|
118
|
+
return await countWithJsTiktoken(tokenizerModel, invocation);
|
|
119
|
+
} catch {}
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
return this.countHeuristic(invocation);
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
countHeuristic(invocation) {
|
|
126
|
+
const estimate = estimateInvocationTokens(invocation);
|
|
127
|
+
return {
|
|
128
|
+
total: estimate.total,
|
|
129
|
+
mode: "heuristic",
|
|
130
|
+
source: "multi_strategy_estimator",
|
|
131
|
+
details: estimate
|
|
132
|
+
};
|
|
133
|
+
}
|
|
134
|
+
}
|
|
@@ -1,59 +1,59 @@
|
|
|
1
|
-
function countMatches(text, pattern) {
|
|
2
|
-
return (text.match(pattern) || []).length;
|
|
3
|
-
}
|
|
4
|
-
|
|
5
|
-
export function estimateTextTokens(text) {
|
|
6
|
-
const raw = String(text || "");
|
|
7
|
-
if (!raw) {
|
|
8
|
-
return {
|
|
9
|
-
total: 0,
|
|
10
|
-
strategies: {
|
|
11
|
-
charBased: 0,
|
|
12
|
-
wordBased: 0,
|
|
13
|
-
denseText: 0,
|
|
14
|
-
cjkAware: 0
|
|
15
|
-
}
|
|
16
|
-
};
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
const words = raw.trim().split(/\s+/).filter(Boolean).length;
|
|
20
|
-
const punctuation = countMatches(raw, /[{}[\]():;,.]/g);
|
|
21
|
-
const operators = countMatches(raw, /[=+\-/*<>|&]/g);
|
|
22
|
-
const quotes = countMatches(raw, /["'`]/g);
|
|
23
|
-
const newlines = countMatches(raw, /\n/g);
|
|
24
|
-
const cjk = countMatches(raw, /[\p{Script=Han}\p{Script=Hiragana}\p{Script=Katakana}\p{Script=Hangul}]/gu);
|
|
25
|
-
const symbolRatio = raw.length ? (punctuation + operators + quotes) / raw.length : 0;
|
|
26
|
-
|
|
27
|
-
const charBased = Math.ceil(raw.length / 4);
|
|
28
|
-
const wordBased = Math.ceil(words * 1.28 + punctuation * 0.18 + operators * 0.2 + newlines * 0.12);
|
|
29
|
-
const denseDivisor = symbolRatio >= 0.12 ? 3.1 : 3.5;
|
|
30
|
-
const denseText = Math.ceil(raw.length / denseDivisor + quotes * 0.08);
|
|
31
|
-
const cjkAware = cjk ? Math.ceil(cjk * 1.15 + (raw.length - cjk) / 4.4) : 0;
|
|
32
|
-
|
|
33
|
-
return {
|
|
34
|
-
total: Math.max(charBased, wordBased, denseText, cjkAware),
|
|
35
|
-
strategies: {
|
|
36
|
-
charBased,
|
|
37
|
-
wordBased,
|
|
38
|
-
denseText,
|
|
39
|
-
cjkAware
|
|
40
|
-
}
|
|
41
|
-
};
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
export function estimateInvocationTokens(invocation) {
|
|
45
|
-
const system = estimateTextTokens(invocation?.system || "");
|
|
46
|
-
const messages = (invocation?.messages || []).map((message) => ({
|
|
47
|
-
role: message.role || "user",
|
|
48
|
-
...estimateTextTokens(message.content || "")
|
|
49
|
-
}));
|
|
50
|
-
const messageTotal = messages.reduce((sum, message) => sum + message.total, 0);
|
|
51
|
-
const framing = invocation?.messages?.length ? invocation.messages.length * 6 : 0;
|
|
52
|
-
|
|
53
|
-
return {
|
|
54
|
-
total: system.total + messageTotal + framing,
|
|
55
|
-
system,
|
|
56
|
-
messages,
|
|
57
|
-
framing
|
|
58
|
-
};
|
|
59
|
-
}
|
|
1
|
+
function countMatches(text, pattern) {
|
|
2
|
+
return (text.match(pattern) || []).length;
|
|
3
|
+
}
|
|
4
|
+
|
|
5
|
+
export function estimateTextTokens(text) {
|
|
6
|
+
const raw = String(text || "");
|
|
7
|
+
if (!raw) {
|
|
8
|
+
return {
|
|
9
|
+
total: 0,
|
|
10
|
+
strategies: {
|
|
11
|
+
charBased: 0,
|
|
12
|
+
wordBased: 0,
|
|
13
|
+
denseText: 0,
|
|
14
|
+
cjkAware: 0
|
|
15
|
+
}
|
|
16
|
+
};
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
const words = raw.trim().split(/\s+/).filter(Boolean).length;
|
|
20
|
+
const punctuation = countMatches(raw, /[{}[\]():;,.]/g);
|
|
21
|
+
const operators = countMatches(raw, /[=+\-/*<>|&]/g);
|
|
22
|
+
const quotes = countMatches(raw, /["'`]/g);
|
|
23
|
+
const newlines = countMatches(raw, /\n/g);
|
|
24
|
+
const cjk = countMatches(raw, /[\p{Script=Han}\p{Script=Hiragana}\p{Script=Katakana}\p{Script=Hangul}]/gu);
|
|
25
|
+
const symbolRatio = raw.length ? (punctuation + operators + quotes) / raw.length : 0;
|
|
26
|
+
|
|
27
|
+
const charBased = Math.ceil(raw.length / 4);
|
|
28
|
+
const wordBased = Math.ceil(words * 1.28 + punctuation * 0.18 + operators * 0.2 + newlines * 0.12);
|
|
29
|
+
const denseDivisor = symbolRatio >= 0.12 ? 3.1 : 3.5;
|
|
30
|
+
const denseText = Math.ceil(raw.length / denseDivisor + quotes * 0.08);
|
|
31
|
+
const cjkAware = cjk ? Math.ceil(cjk * 1.15 + (raw.length - cjk) / 4.4) : 0;
|
|
32
|
+
|
|
33
|
+
return {
|
|
34
|
+
total: Math.max(charBased, wordBased, denseText, cjkAware),
|
|
35
|
+
strategies: {
|
|
36
|
+
charBased,
|
|
37
|
+
wordBased,
|
|
38
|
+
denseText,
|
|
39
|
+
cjkAware
|
|
40
|
+
}
|
|
41
|
+
};
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
export function estimateInvocationTokens(invocation) {
|
|
45
|
+
const system = estimateTextTokens(invocation?.system || "");
|
|
46
|
+
const messages = (invocation?.messages || []).map((message) => ({
|
|
47
|
+
role: message.role || "user",
|
|
48
|
+
...estimateTextTokens(message.content || "")
|
|
49
|
+
}));
|
|
50
|
+
const messageTotal = messages.reduce((sum, message) => sum + message.total, 0);
|
|
51
|
+
const framing = invocation?.messages?.length ? invocation.messages.length * 6 : 0;
|
|
52
|
+
|
|
53
|
+
return {
|
|
54
|
+
total: system.total + messageTotal + framing,
|
|
55
|
+
system,
|
|
56
|
+
messages,
|
|
57
|
+
framing
|
|
58
|
+
};
|
|
59
|
+
}
|