@hilbras/omninode 2.0.0-alpha.6 → 2.0.0-alpha.7
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 +8 -2
- package/dist/cli/index.js +117 -4
- package/dist/cli/index.js.map +1 -1
- package/dist/index.d.ts +71 -14
- package/dist/index.js +117 -4
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -18,9 +18,9 @@ existing intelligence; it does not try to become another model.
|
|
|
18
18
|
|
|
19
19
|
## Status
|
|
20
20
|
|
|
21
|
-
**v2.0.0-alpha.
|
|
21
|
+
**v2.0.0-alpha.7 — v2 Phase 7: OmniHilbras Integration v2**
|
|
22
22
|
(the reliability & interoperability line: architecture → execution →
|
|
23
|
-
protocol → adapters → pipelines → providers) (the v2
|
|
23
|
+
protocol → adapters → pipelines → providers → OmniHilbras) (the v2
|
|
24
24
|
roadmap is [docs/ROADMAP_V2.md](docs/ROADMAP_V2.md); the v1 plan is complete:
|
|
25
25
|
the
|
|
26
26
|
full loop from §28 runs in one command —
|
|
@@ -31,6 +31,12 @@ for the plan and [docs/ARCHITECTURE.md](docs/ARCHITECTURE.md) for the design).
|
|
|
31
31
|
|
|
32
32
|
### What's new in v2 (so far)
|
|
33
33
|
|
|
34
|
+
- **Phase 7 — OmniHilbras Integration v2**: the dedicated adapter now
|
|
35
|
+
captures gateway-level metadata (version, tier, region) and attaches it
|
|
36
|
+
to discovered models, honors provider-level `timeout_ms`, and supports
|
|
37
|
+
streaming completions (`stream()` over SSE) — while a guard test proves
|
|
38
|
+
the core references OmniHilbras *only* through the provider layer, and a
|
|
39
|
+
full pipeline runs with no OmniHilbras configured at all.
|
|
34
40
|
- **Phase 6 — Provider Infrastructure v2**: standardized provider contract
|
|
35
41
|
(`providerId`, secret-free `authentication`, declared `capabilities`,
|
|
36
42
|
`connect()` / `getModel()` discovery), richer model metadata (context
|
package/dist/cli/index.js
CHANGED
|
@@ -6,7 +6,7 @@ import { realpathSync } from "fs";
|
|
|
6
6
|
import { Command } from "commander";
|
|
7
7
|
|
|
8
8
|
// src/version.ts
|
|
9
|
-
var OMNINODE_VERSION = "2.0.0-alpha.
|
|
9
|
+
var OMNINODE_VERSION = "2.0.0-alpha.7";
|
|
10
10
|
|
|
11
11
|
// src/errors/index.ts
|
|
12
12
|
var OmniNodeError = class extends Error {
|
|
@@ -68,6 +68,7 @@ var providerConfigSchema = z.object({
|
|
|
68
68
|
type: z.enum(["openai-compatible", "omnihilbras", "openrouter", "local", "custom"]),
|
|
69
69
|
base_url: z.string().url(),
|
|
70
70
|
api_key_env_var: z.string().min(1).optional(),
|
|
71
|
+
timeout_ms: z.number().int().positive().optional(),
|
|
71
72
|
headers: z.record(z.string()).optional(),
|
|
72
73
|
enabled: z.boolean().optional(),
|
|
73
74
|
metadata: z.record(z.unknown()).optional()
|
|
@@ -132,7 +133,8 @@ var projectConfigSchema = z.object({
|
|
|
132
133
|
memory: z.object({
|
|
133
134
|
provider: z.string().min(1).default("local"),
|
|
134
135
|
base_url: z.string().url().optional(),
|
|
135
|
-
api_key_env_var: z.string().min(1).optional()
|
|
136
|
+
api_key_env_var: z.string().min(1).optional(),
|
|
137
|
+
timeout_ms: z.number().int().positive().optional()
|
|
136
138
|
}).strict().optional()
|
|
137
139
|
}).strict();
|
|
138
140
|
var appConfigSchema = z.object({
|
|
@@ -245,6 +247,7 @@ function toProviderConfig(raw) {
|
|
|
245
247
|
type: raw.type,
|
|
246
248
|
baseUrl: raw.base_url,
|
|
247
249
|
apiKeyEnvVar: raw.api_key_env_var,
|
|
250
|
+
timeoutMs: raw.timeout_ms,
|
|
248
251
|
headers: raw.headers,
|
|
249
252
|
enabled: raw.enabled,
|
|
250
253
|
metadata: raw.metadata
|
|
@@ -546,6 +549,7 @@ function providerTransportError(context) {
|
|
|
546
549
|
}
|
|
547
550
|
|
|
548
551
|
// src/providers/openai-compatible/index.ts
|
|
552
|
+
var DEFAULT_REQUEST_TIMEOUT_MS = 3e4;
|
|
549
553
|
var OpenAICompatibleProvider = class {
|
|
550
554
|
config;
|
|
551
555
|
providerId;
|
|
@@ -565,6 +569,10 @@ var OpenAICompatibleProvider = class {
|
|
|
565
569
|
};
|
|
566
570
|
this.log = log.child({ provider: config.name });
|
|
567
571
|
}
|
|
572
|
+
/** Provider-level request timeout (roadmap §6.6/§11) with the shared default. */
|
|
573
|
+
get requestTimeoutMs() {
|
|
574
|
+
return this.config.timeoutMs ?? DEFAULT_REQUEST_TIMEOUT_MS;
|
|
575
|
+
}
|
|
568
576
|
/** Authenticate + discover (roadmap §10, Model Discovery). */
|
|
569
577
|
async connect() {
|
|
570
578
|
authHeaders(this.config, this.env);
|
|
@@ -576,11 +584,17 @@ var OpenAICompatibleProvider = class {
|
|
|
576
584
|
const models = this.discovered ?? await this.connect();
|
|
577
585
|
return models.find((m) => m.id === modelId);
|
|
578
586
|
}
|
|
579
|
-
|
|
587
|
+
/** Raw /models body (protected so adapters can capture gateway-level metadata). */
|
|
588
|
+
async rawModelsResponse() {
|
|
589
|
+
const response = await this.modelsRequest();
|
|
590
|
+
return response.body;
|
|
591
|
+
}
|
|
592
|
+
async modelsRequest() {
|
|
580
593
|
let response;
|
|
581
594
|
try {
|
|
582
595
|
response = await requestJson(`${this.config.baseUrl}/models`, {
|
|
583
|
-
headers: authHeaders(this.config, this.env)
|
|
596
|
+
headers: authHeaders(this.config, this.env),
|
|
597
|
+
timeoutMs: this.requestTimeoutMs
|
|
584
598
|
});
|
|
585
599
|
} catch (error) {
|
|
586
600
|
if (error instanceof ProviderError) throw error;
|
|
@@ -595,6 +609,10 @@ var OpenAICompatibleProvider = class {
|
|
|
595
609
|
body
|
|
596
610
|
});
|
|
597
611
|
}
|
|
612
|
+
return response;
|
|
613
|
+
}
|
|
614
|
+
async listModels() {
|
|
615
|
+
const body = await this.rawModelsResponse();
|
|
598
616
|
const entries = extractModelEntries(body, this.config);
|
|
599
617
|
const models = [];
|
|
600
618
|
let skipped = 0;
|
|
@@ -637,6 +655,10 @@ var OpenAICompatibleProvider = class {
|
|
|
637
655
|
}
|
|
638
656
|
};
|
|
639
657
|
}
|
|
658
|
+
/** Streaming where the gateway supports it (§11). */
|
|
659
|
+
stream(request, onChunk) {
|
|
660
|
+
return streamChatCompletion(this.config, request, onChunk, this.env);
|
|
661
|
+
}
|
|
640
662
|
async healthCheck() {
|
|
641
663
|
const lastCheckedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
642
664
|
try {
|
|
@@ -671,6 +693,7 @@ var OpenAICompatibleProvider = class {
|
|
|
671
693
|
response = await requestJson(`${this.config.baseUrl}/chat/completions`, {
|
|
672
694
|
method: "POST",
|
|
673
695
|
headers: authHeaders(this.config, this.env),
|
|
696
|
+
timeoutMs: this.requestTimeoutMs,
|
|
674
697
|
body: {
|
|
675
698
|
model: request.model,
|
|
676
699
|
messages: request.messages,
|
|
@@ -715,6 +738,74 @@ var OpenAICompatibleProvider = class {
|
|
|
715
738
|
};
|
|
716
739
|
}
|
|
717
740
|
};
|
|
741
|
+
async function streamChatCompletion(config, request, onChunk, env = process.env) {
|
|
742
|
+
const baseUrl = normalizeBaseUrl(config.baseUrl);
|
|
743
|
+
const timeoutMs = config.timeoutMs ?? DEFAULT_REQUEST_TIMEOUT_MS;
|
|
744
|
+
let response;
|
|
745
|
+
try {
|
|
746
|
+
response = await fetch(`${baseUrl}/chat/completions`, {
|
|
747
|
+
method: "POST",
|
|
748
|
+
headers: {
|
|
749
|
+
accept: "text/event-stream",
|
|
750
|
+
"content-type": "application/json",
|
|
751
|
+
...authHeaders(config, env)
|
|
752
|
+
},
|
|
753
|
+
body: JSON.stringify({
|
|
754
|
+
model: request.model,
|
|
755
|
+
messages: request.messages,
|
|
756
|
+
stream: true,
|
|
757
|
+
...request.temperature !== void 0 ? { temperature: request.temperature } : {},
|
|
758
|
+
...request.maxTokens !== void 0 ? { max_tokens: request.maxTokens } : {}
|
|
759
|
+
}),
|
|
760
|
+
signal: AbortSignal.timeout(timeoutMs)
|
|
761
|
+
});
|
|
762
|
+
} catch (error) {
|
|
763
|
+
throw providerTransportError({ provider: config.name, operation: "stream", cause: error });
|
|
764
|
+
}
|
|
765
|
+
if (!response.ok) {
|
|
766
|
+
const body = await response.json().catch(() => void 0);
|
|
767
|
+
throw providerHttpError({ provider: config.name, operation: "stream", status: response.status, body });
|
|
768
|
+
}
|
|
769
|
+
if (!response.body) {
|
|
770
|
+
throw providerHttpError({
|
|
771
|
+
provider: config.name,
|
|
772
|
+
operation: "stream",
|
|
773
|
+
status: 500,
|
|
774
|
+
body: { error: { message: "streaming is not supported by this endpoint" } }
|
|
775
|
+
});
|
|
776
|
+
}
|
|
777
|
+
let content = "";
|
|
778
|
+
let model = request.model;
|
|
779
|
+
let finishReason;
|
|
780
|
+
const decoder = new TextDecoder();
|
|
781
|
+
let buffer = "";
|
|
782
|
+
for await (const piece of response.body) {
|
|
783
|
+
buffer += decoder.decode(piece, { stream: true });
|
|
784
|
+
let newline = buffer.indexOf("\n");
|
|
785
|
+
while (newline >= 0) {
|
|
786
|
+
const line = buffer.slice(0, newline).trim();
|
|
787
|
+
buffer = buffer.slice(newline + 1);
|
|
788
|
+
newline = buffer.indexOf("\n");
|
|
789
|
+
if (line.length === 0 || line.startsWith(":")) continue;
|
|
790
|
+
const data = line.startsWith("data:") ? line.slice(5).trim() : line;
|
|
791
|
+
if (data === "[DONE]") continue;
|
|
792
|
+
let event;
|
|
793
|
+
try {
|
|
794
|
+
event = JSON.parse(data);
|
|
795
|
+
} catch {
|
|
796
|
+
continue;
|
|
797
|
+
}
|
|
798
|
+
if (typeof event.model === "string") model = event.model;
|
|
799
|
+
const choices = Array.isArray(event.choices) ? event.choices : [];
|
|
800
|
+
const first = choices[0];
|
|
801
|
+
const delta = typeof first?.delta?.content === "string" ? first.delta.content : "";
|
|
802
|
+
if (delta.length > 0) content += delta;
|
|
803
|
+
if (typeof first?.finish_reason === "string") finishReason = first.finish_reason;
|
|
804
|
+
onChunk({ delta, ...finishReason !== void 0 ? { finishReason } : {}, raw: event });
|
|
805
|
+
}
|
|
806
|
+
}
|
|
807
|
+
return { model, content, ...finishReason !== void 0 ? { finishReason } : {} };
|
|
808
|
+
}
|
|
718
809
|
function normalizeModalities(value) {
|
|
719
810
|
const list = Array.isArray(value) ? value : typeof value === "string" ? [value] : void 0;
|
|
720
811
|
if (!list) return void 0;
|
|
@@ -748,10 +839,32 @@ function inferHealth(error) {
|
|
|
748
839
|
|
|
749
840
|
// src/providers/omnihilbras/index.ts
|
|
750
841
|
var OmniHilbrasProvider = class extends OpenAICompatibleProvider {
|
|
842
|
+
gateway;
|
|
843
|
+
async listModels() {
|
|
844
|
+
const raw = await this.rawModelsResponse();
|
|
845
|
+
await this.extractGatewayMetadata(raw);
|
|
846
|
+
const models = await super.listModels();
|
|
847
|
+
return this.gateway ? models.map((model) => ({ ...model, metadata: { ...model.metadata, gateway: this.gateway } })) : models;
|
|
848
|
+
}
|
|
751
849
|
/**
|
|
752
850
|
* Run the full connect flow. Throws on authentication or transport failure
|
|
753
851
|
* (use healthCheck() for a non-throwing probe).
|
|
754
852
|
*/
|
|
853
|
+
/**
|
|
854
|
+
* Gateway metadata reported alongside the model list (gateway version, tier,
|
|
855
|
+
* region…), kept separate from per-model metadata (§11 — provider metadata).
|
|
856
|
+
*/
|
|
857
|
+
gatewayMetadata() {
|
|
858
|
+
return this.gateway;
|
|
859
|
+
}
|
|
860
|
+
async extractGatewayMetadata(body) {
|
|
861
|
+
if (body !== null && typeof body === "object") {
|
|
862
|
+
const gateway = body.gateway;
|
|
863
|
+
if (gateway !== null && typeof gateway === "object") {
|
|
864
|
+
this.gateway = gateway;
|
|
865
|
+
}
|
|
866
|
+
}
|
|
867
|
+
}
|
|
755
868
|
async connectAndRegister(registry) {
|
|
756
869
|
const apiKey = resolveApiKey(this.config);
|
|
757
870
|
const discovered = await this.listModels();
|