mycomesh-consumer 0.1.44 → 0.1.46

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "mycomesh-consumer",
3
- "version": "0.1.44",
3
+ "version": "0.1.46",
4
4
  "description": "Native Node.js MycoMesh Consumer and OpenAI-compatible request CLI.",
5
5
  "publishConfig": {
6
6
  "access": "public"
@@ -87,6 +87,7 @@ const TUNNEL_START_TIMEOUT_MS = 30_000;
87
87
  const TUNNEL_STOP_TIMEOUT_MS = 2_000;
88
88
  const RELAY_HEALTH_CACHE_MS = 30_000;
89
89
  const RELAY_HEALTH_STALE_MS = 10 * 60_000;
90
+ const RELAY_HEALTH_RETRY_DELAY_MS = 250;
90
91
 
91
92
  const DEFAULT_NETWORK = Object.freeze({
92
93
  chain_id: DEFAULT_CHAIN_ID,
@@ -582,7 +583,7 @@ export class NativeConsumerState {
582
583
  this.maxFeeUnits = options.maxFeeUnits || Number(env.MYCOMESH_V8_MAX_FEE_UNITS || DEFAULT_MAX_FEE_UNITS);
583
584
  if (!Number.isSafeInteger(this.maxFeeUnits) || this.maxFeeUnits <= 0) throw new Error("max fee must be a positive integer");
584
585
  this.timeoutMs = options.timeoutMs ?? Number(env.MYCOMESH_V8_REQUEST_TIMEOUT_SECONDS || 300) * 1000;
585
- this.healthTimeoutMs = options.healthTimeoutMs ?? Number(env.MYCOMESH_V8_HEALTH_TIMEOUT_SECONDS || 5) * 1000;
586
+ this.healthTimeoutMs = options.healthTimeoutMs ?? Number(env.MYCOMESH_V8_HEALTH_TIMEOUT_SECONDS || 10) * 1000;
586
587
  this.network = parseNetworkConfig(options.networkConfig || env.MYCOMESH_CONSUMER_NETWORK_CONFIG);
587
588
  if (env.MYCOMESH_CONSUMER_SETTLEMENT_RPC_URLS) {
588
589
  this.network.rpc_urls = String(env.MYCOMESH_CONSUMER_SETTLEMENT_RPC_URLS).split(",").map((item) => item.trim()).filter(Boolean);
@@ -901,13 +902,21 @@ export class NativeConsumerState {
901
902
  if (cached && !refresh && cacheAge < RELAY_HEALTH_CACHE_MS) return cached.payload;
902
903
  const request = { dispatcher: this.dispatcher, headers: { accept: "application/json" } };
903
904
  let payload;
904
- try {
905
- const response = await fetchWithTimeout(`${relayUrl}/relay/health`, request, this.healthTimeoutMs);
906
- payload = await readJsonResponse(response);
907
- if (!response.ok || payload?.ok !== true) throw new Error(`Relay health is invalid for ${relayUrl}`);
908
- } catch (error) {
905
+ let lastError;
906
+ for (let attempt = 0; attempt < 2 && !payload; attempt += 1) {
907
+ try {
908
+ const response = await fetchWithTimeout(`${relayUrl}/relay/health`, request, this.healthTimeoutMs);
909
+ payload = await readJsonResponse(response);
910
+ if (!response.ok || payload?.ok !== true) throw new Error(`Relay health is invalid for ${relayUrl}`);
911
+ } catch (error) {
912
+ lastError = error;
913
+ payload = undefined;
914
+ if (attempt === 0) await new Promise((resolve) => setTimeout(resolve, RELAY_HEALTH_RETRY_DELAY_MS));
915
+ }
916
+ }
917
+ if (!payload) {
909
918
  if (cached && cacheAge < RELAY_HEALTH_STALE_MS) return cached.payload;
910
- throw error;
919
+ throw lastError || new Error(`Relay health is unavailable for ${relayUrl}`);
911
920
  }
912
921
  const v8 = payload.v8;
913
922
  if (!v8 || v8.enabled !== true || Number(v8.providers || 0) <= 0) throw new Error(`Relay has no live Settlement V8 Provider: ${relayUrl}`);
@@ -996,7 +1005,7 @@ export class NativeConsumerState {
996
1005
  this.recordReceipt(selected.relayUrl, path, payment.model, settlement);
997
1006
  headers["PAYMENT-RESPONSE"] = paymentResponse;
998
1007
  }
999
- return { payload, status: 200, headers };
1008
+ return { payload: restoreClientResponse(payload, body), status: 200, headers };
1000
1009
  } catch (error) {
1001
1010
  lastError = error.message;
1002
1011
  }
@@ -1020,6 +1029,49 @@ function normalizeOpenAiError(value, fallbackType) {
1020
1029
  return { ...value, error: { ...value.error, message: String(value.error.message || fallbackType), type: String(value.error.type || fallbackType), param: value.error.param ?? null, code: value.error.code || value.error.type || fallbackType } };
1021
1030
  }
1022
1031
 
1032
+ function clientToolSchema(tools, name) {
1033
+ if (!Array.isArray(tools)) return undefined;
1034
+ for (const tool of tools) {
1035
+ if (!tool || typeof tool !== "object") continue;
1036
+ const definition = tool.function && typeof tool.function === "object" ? tool.function : tool;
1037
+ if (definition.name === name && definition.parameters && typeof definition.parameters === "object") return definition.parameters;
1038
+ }
1039
+ return undefined;
1040
+ }
1041
+
1042
+ function orderBySchema(value, schema) {
1043
+ if (Array.isArray(value)) return value.map((item) => orderBySchema(item, schema?.items));
1044
+ if (!value || typeof value !== "object" || Array.isArray(schema)) return value;
1045
+ const properties = schema?.properties;
1046
+ if (!properties || typeof properties !== "object") return value;
1047
+ const ordered = {};
1048
+ for (const key of Object.keys(properties)) {
1049
+ if (Object.prototype.hasOwnProperty.call(value, key)) ordered[key] = orderBySchema(value[key], properties[key]);
1050
+ }
1051
+ for (const key of Object.keys(value)) {
1052
+ if (!Object.prototype.hasOwnProperty.call(ordered, key)) ordered[key] = value[key];
1053
+ }
1054
+ return ordered;
1055
+ }
1056
+
1057
+ function restoreClientResponse(payload, body) {
1058
+ if (!payload || typeof payload !== "object") return payload;
1059
+ if (typeof body.model === "string" && body.model) payload.model = body.model;
1060
+ const calls = [];
1061
+ for (const choice of Array.isArray(payload.choices) ? payload.choices : []) {
1062
+ calls.push(...(Array.isArray(choice?.message?.tool_calls) ? choice.message.tool_calls : []));
1063
+ }
1064
+ calls.push(...(Array.isArray(payload.output) ? payload.output.filter((item) => item?.type === "function_call") : []));
1065
+ for (const call of calls) {
1066
+ const fn = call?.function && typeof call.function === "object" ? call.function : call;
1067
+ if (typeof fn?.name !== "string" || typeof fn.arguments !== "string") continue;
1068
+ const schema = clientToolSchema(body.tools, fn.name);
1069
+ if (!schema) continue;
1070
+ try { fn.arguments = JSON.stringify(orderBySchema(JSON.parse(fn.arguments), schema)); } catch {}
1071
+ }
1072
+ return payload;
1073
+ }
1074
+
1023
1075
  function decodeRequestBody(request) {
1024
1076
  return new Promise((resolve, reject) => {
1025
1077
  let size = 0;
package/src/consumer.mjs CHANGED
@@ -11,7 +11,7 @@ import {
11
11
  createConsumerServer,
12
12
  } from "./consumer-runtime.mjs";
13
13
 
14
- export const CONSUMER_RELEASE_VERSION = "0.1.44";
14
+ export const CONSUMER_RELEASE_VERSION = "0.1.46";
15
15
  export const API_COMMANDS = new Set(["health", "models", "responses", "chat"]);
16
16
  const API_VALUE_OPTIONS = new Set([
17
17
  "--base-url",