@takara-ai/miru-code 0.8.0 → 1.0.3
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/README.md +42 -1
- package/package.json +1 -1
- package/src/embeddings/openai.ts +43 -4
- package/src/embeddings/validate.ts +13 -4
- package/src/help.ts +2 -0
package/README.md
CHANGED
|
@@ -114,7 +114,46 @@ Pass the **project root** as `repo` for local projects. Prefer these over Grep,
|
|
|
114
114
|
|
|
115
115
|
Hybrid search: Takara embeddings + BM25 + fusion + reranking. Indexes **code**, **docs**, **config**, or **all** via `--content`.
|
|
116
116
|
|
|
117
|
-
Disk cache: `~/Library/Caches/miru` (macOS), `~/.cache/miru` (Linux). MCP watches local files and updates incrementally; run `miru clear .` after big refactors when using CLI only.
|
|
117
|
+
Disk cache: `~/Library/Caches/miru` (macOS), `~/.cache/miru` (Linux). MCP watches local files and updates incrementally; run `miru clear .` after big refactors when using CLI only. Upgrading Miru invalidates stale indexes automatically when the package version epoch changes.
|
|
118
|
+
|
|
119
|
+
### Chunking & languages
|
|
120
|
+
|
|
121
|
+
Miru chunks source in tiers: **AST** (tree-sitter, default) → **structural** heuristics → **line** splits.
|
|
122
|
+
|
|
123
|
+
**AST chunking** — 22 languages (syntax-aware boundaries via vendored `web-tree-sitter` grammars):
|
|
124
|
+
|
|
125
|
+
| Language | Typical extensions |
|
|
126
|
+
|----------|-------------------|
|
|
127
|
+
| bash | `.sh`, `.bash`, `.zsh` |
|
|
128
|
+
| c | `.c` |
|
|
129
|
+
| cpp | `.cpp`, `.h`, `.hpp`, etc. |
|
|
130
|
+
| csharp | `.cs` |
|
|
131
|
+
| css | `.css` |
|
|
132
|
+
| dart | `.dart` |
|
|
133
|
+
| elixir | `.ex`, `.exs` |
|
|
134
|
+
| embeddedtemplate | ERB-style templates |
|
|
135
|
+
| go | `.go` |
|
|
136
|
+
| haskell | `.hs` |
|
|
137
|
+
| html | `.html` |
|
|
138
|
+
| java | `.java` |
|
|
139
|
+
| javascript | `.js`, `.jsx`, `.mjs`, `.cjs` |
|
|
140
|
+
| json | `.json` |
|
|
141
|
+
| ocaml | `.ml`, etc. |
|
|
142
|
+
| php | `.php` |
|
|
143
|
+
| python | `.py`, `.pyi` |
|
|
144
|
+
| ruby | `.rb` |
|
|
145
|
+
| rust | `.rs` |
|
|
146
|
+
| scala | `.scala` |
|
|
147
|
+
| solidity | `.sol` |
|
|
148
|
+
| typescript | `.ts`, `.mts`, `.cts` |
|
|
149
|
+
|
|
150
|
+
**Also shipped:** `.tsx` uses a dedicated TSX grammar; extra OCaml and PHP-only grammars are vendored alongside the main set.
|
|
151
|
+
|
|
152
|
+
**Structural fallback** (brace/indent heuristics when AST is unavailable): python, go, typescript, javascript, cpp, c.
|
|
153
|
+
|
|
154
|
+
**Line fallback:** everything else that gets indexed (kotlin, swift, vue, sql, etc.) — still searchable, coarser chunks.
|
|
155
|
+
|
|
156
|
+
Set `MIRU_AST_CHUNKING=0` to disable AST and use structural → lines only.
|
|
118
157
|
|
|
119
158
|
## CLI reference
|
|
120
159
|
|
|
@@ -149,10 +188,12 @@ const results = await index.search({ query: "BM25 tokenize", topK: 10 });
|
|
|
149
188
|
|----------|-------|
|
|
150
189
|
| `TAKARA_API_KEY` | Required |
|
|
151
190
|
| `MIRU_OPENAI_BASE_URL` | Default `https://infer.takara.ai/v1` |
|
|
191
|
+
| `MIRU_OPENAI_EMBEDDING_MODEL` | Default `ds1-miru-int8` |
|
|
152
192
|
| `MIRU_WORKSPACE_ROOT` | Restrict MCP local `repo` paths to this directory |
|
|
153
193
|
| `MIRU_MAX_INDEX_FILES` | Cap files indexed per operation |
|
|
154
194
|
| `MIRU_ALLOW_HTTP_GIT` | Set `1` to allow plain `http://` git clones |
|
|
155
195
|
| `MIRU_MCP_WATCH` | Set `0` to disable MCP file watch |
|
|
196
|
+
| `MIRU_AST_CHUNKING` | Set `0` to disable tree-sitter AST chunking |
|
|
156
197
|
| `NO_COLOR` | Disable CLI colors |
|
|
157
198
|
|
|
158
199
|
See `.env.example` for more.
|
package/package.json
CHANGED
package/src/embeddings/openai.ts
CHANGED
|
@@ -1,15 +1,16 @@
|
|
|
1
1
|
import { mapPool, resolveWorkerConcurrency } from "../concurrency.ts";
|
|
2
2
|
import { envFirstString, envOptionalInt, resolveEmbeddingApiKey } from "../env.ts";
|
|
3
3
|
|
|
4
|
-
const DEFAULT_MODEL = "ds1-
|
|
4
|
+
const DEFAULT_MODEL = "ds1-miru-int8";
|
|
5
5
|
const DEFAULT_BASE_URL = "https://infer.takara.ai/v1";
|
|
6
6
|
const DEFAULT_BATCH_SIZE = 32;
|
|
7
7
|
const DEFAULT_MAX_EMBED_CHARS = 1300;
|
|
8
8
|
const WINDOW_OVERLAP_CHARS = 120;
|
|
9
9
|
|
|
10
|
-
/** Native output size
|
|
10
|
+
/** Native output size per model (override with MIRU_EMBEDDING_DIMENSIONS). */
|
|
11
11
|
const MODEL_DEFAULT_DIMENSIONS: Record<string, number> = {
|
|
12
12
|
"ds1-potion-code-16m": 256,
|
|
13
|
+
"ds1-miru-int8": 256,
|
|
13
14
|
};
|
|
14
15
|
|
|
15
16
|
export interface EmbeddingTransportStats {
|
|
@@ -123,9 +124,47 @@ export function resolveEmbeddingBaseUrl(): string {
|
|
|
123
124
|
);
|
|
124
125
|
}
|
|
125
126
|
|
|
127
|
+
interface Int8Embedding {
|
|
128
|
+
dtype: "i8";
|
|
129
|
+
values: number[];
|
|
130
|
+
scale: number;
|
|
131
|
+
zero_point: number;
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
type EmbeddingPayload = number[] | Int8Embedding;
|
|
135
|
+
|
|
126
136
|
interface EmbeddingResponseItem {
|
|
127
137
|
index: number;
|
|
128
|
-
embedding:
|
|
138
|
+
embedding: EmbeddingPayload;
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
function isInt8Embedding(embedding: EmbeddingPayload): embedding is Int8Embedding {
|
|
142
|
+
return (
|
|
143
|
+
typeof embedding === "object" &&
|
|
144
|
+
embedding !== null &&
|
|
145
|
+
!Array.isArray(embedding) &&
|
|
146
|
+
embedding.dtype === "i8" &&
|
|
147
|
+
Array.isArray(embedding.values)
|
|
148
|
+
);
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
export function embeddingDimensions(embedding: EmbeddingPayload): number {
|
|
152
|
+
return isInt8Embedding(embedding) ? embedding.values.length : embedding.length;
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
export function dequantizeEmbedding(embedding: EmbeddingPayload): Float32Array {
|
|
156
|
+
if (Array.isArray(embedding)) {
|
|
157
|
+
return new Float32Array(embedding);
|
|
158
|
+
}
|
|
159
|
+
if (isInt8Embedding(embedding)) {
|
|
160
|
+
const { values, scale, zero_point } = embedding;
|
|
161
|
+
const out = new Float32Array(values.length);
|
|
162
|
+
for (let i = 0; i < values.length; i++) {
|
|
163
|
+
out[i] = ((values[i] ?? 0) - zero_point) * scale;
|
|
164
|
+
}
|
|
165
|
+
return out;
|
|
166
|
+
}
|
|
167
|
+
throw new Error("Embedding API returned unsupported embedding format");
|
|
129
168
|
}
|
|
130
169
|
|
|
131
170
|
interface EmbeddingResponse {
|
|
@@ -402,7 +441,7 @@ function vectorsFromResponse(data: EmbeddingResponseItem[], expected: number): F
|
|
|
402
441
|
const byIndex = new Map<number, Float32Array>();
|
|
403
442
|
for (const item of data) {
|
|
404
443
|
if (item.index >= 0 && item.index < expected) {
|
|
405
|
-
byIndex.set(item.index,
|
|
444
|
+
byIndex.set(item.index, dequantizeEmbedding(item.embedding));
|
|
406
445
|
}
|
|
407
446
|
}
|
|
408
447
|
if (byIndex.size !== expected) {
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import {
|
|
2
|
+
embeddingDimensions,
|
|
2
3
|
resolveEmbeddingBaseUrl,
|
|
3
4
|
resolveEmbeddingDimensions,
|
|
4
5
|
resolveEmbeddingModel,
|
|
@@ -65,21 +66,29 @@ export async function validateEmbeddingApiKey(options: {
|
|
|
65
66
|
|
|
66
67
|
try {
|
|
67
68
|
const payload = (await response.json()) as {
|
|
68
|
-
data?: Array<{ embedding?:
|
|
69
|
+
data?: Array<{ embedding?: Parameters<typeof embeddingDimensions>[0] }>;
|
|
69
70
|
};
|
|
70
71
|
const embedding = payload.data?.[0]?.embedding;
|
|
71
|
-
if (!embedding
|
|
72
|
+
if (!embedding) {
|
|
72
73
|
return {
|
|
73
74
|
valid: false,
|
|
74
75
|
status: response.status,
|
|
75
76
|
message: "Embedding API returned an empty response.",
|
|
76
77
|
};
|
|
77
78
|
}
|
|
78
|
-
|
|
79
|
+
const dim = embeddingDimensions(embedding);
|
|
80
|
+
if (dim === 0) {
|
|
79
81
|
return {
|
|
80
82
|
valid: false,
|
|
81
83
|
status: response.status,
|
|
82
|
-
message:
|
|
84
|
+
message: "Embedding API returned an empty response.",
|
|
85
|
+
};
|
|
86
|
+
}
|
|
87
|
+
if (dimensions != null && dim !== dimensions) {
|
|
88
|
+
return {
|
|
89
|
+
valid: false,
|
|
90
|
+
status: response.status,
|
|
91
|
+
message: `Expected ${dimensions} embedding dimensions, got ${dim}.`,
|
|
83
92
|
};
|
|
84
93
|
}
|
|
85
94
|
} catch {
|
package/src/help.ts
CHANGED
|
@@ -55,6 +55,8 @@ export function printEnvHelp(): void {
|
|
|
55
55
|
writeStdout(" Takara bearer token for embeddings");
|
|
56
56
|
writeStdout(" MIRU_OPENAI_BASE_URL");
|
|
57
57
|
writeStdout(" Default: https://infer.takara.ai/v1");
|
|
58
|
+
writeStdout(" MIRU_OPENAI_EMBEDDING_MODEL");
|
|
59
|
+
writeStdout(" Default: ds1-miru-int8");
|
|
58
60
|
writeStdout(" MIRU_CONCURRENCY");
|
|
59
61
|
writeStdout(" Parallel workers (default: CPUs − 2)");
|
|
60
62
|
writeStdout("");
|