@camstack/addon-ai 0.2.17 → 0.2.18

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/dist/index.js CHANGED
@@ -4,7 +4,7 @@ Object.defineProperties(exports, {
4
4
  });
5
5
  const require_addon = require("./addon.js");
6
6
  const require_execute_plan = require("./execute-plan-gwFcJjeV.js");
7
- const require_usage_store = require("./usage-store-DK494_zK.js");
7
+ const require_usage_store = require("./usage-store-8hfWI98H.js");
8
8
  const require_ensure_llama_server = require("./ensure-llama-server-CwEkUUrD.js");
9
9
  exports.AI_ADDON_ID = require_addon.AI_ADDON_ID;
10
10
  exports.AiAddon = require_addon.AiAddon;
package/dist/index.mjs CHANGED
@@ -1,5 +1,5 @@
1
1
  import { AiAddon, C as googleAdapter, S as openAiAdapter, T as openAiCompatibleAdapter, _ as redactProfileForRead, a as createLlmRuntimeProvider, b as SEED_PROFILE_ID, c as LLM_MODEL_CATALOG, d as createRuntimeClient, f as AI_ADDON_ID, g as mergeProfileSecrets, h as REDACTED_MARKER, i as assembleAi, l as catalogById, m as resolveProfile, n as createMemorySettingsStorePort, p as createLlmProvider, r as LlamaSupervisor, s as createDefaultModelOps, t as createApiSettingsStorePort, u as entryForRef, v as PROFILES_COLLECTION, w as anthropicAdapter, x as DefaultsStore, y as ProfileStore } from "./addon.mjs";
2
2
  import { t as createFetchExecutor } from "./execute-plan-KbxnOqY1.mjs";
3
- import { n as USAGE_COLLECTION, r as UsageStore, t as BUDGETS_COLLECTION } from "./usage-store-BVa_imkv.mjs";
3
+ import { n as USAGE_COLLECTION, r as UsageStore, t as BUDGETS_COLLECTION } from "./usage-store-1FrTJdpU.mjs";
4
4
  import { i as llamaServerAsset, n as ensureLlamaServer, t as LLAMA_SERVER_VERSION } from "./ensure-llama-server-Dmh3cZ41.mjs";
5
5
  export { AI_ADDON_ID, AiAddon, AiAddon as default, BUDGETS_COLLECTION, DefaultsStore, LLAMA_SERVER_VERSION, LLM_MODEL_CATALOG, LlamaSupervisor, PROFILES_COLLECTION, ProfileStore, REDACTED_MARKER, SEED_PROFILE_ID, USAGE_COLLECTION, UsageStore, anthropicAdapter, assembleAi, catalogById, createApiSettingsStorePort, createDefaultModelOps, createFetchExecutor, createLlmProvider, createLlmRuntimeProvider, createMemorySettingsStorePort, createRuntimeClient, ensureLlamaServer, entryForRef, googleAdapter, llamaServerAsset, mergeProfileSecrets, openAiAdapter, openAiCompatibleAdapter, redactProfileForRead, resolveProfile };
@@ -95,12 +95,31 @@ var UsageRowSchema = object({
95
95
  function dayKey(ts) {
96
96
  return new Date(ts).toISOString().slice(0, 10);
97
97
  }
98
+ /**
99
+ * SQL `NULL` is how a relational store spells "this row has no value here" —
100
+ * an absent KEY is how Zod's `.optional()` spells it. They are not the same
101
+ * word, and every usage row uses both: an OK row has `errorCode: null`, an
102
+ * error row has `profileId/kind/model: null`.
103
+ *
104
+ * Without this, `UsageRowSchema.safeParse` rejected EVERY row ever written and
105
+ * `llm.getUsage` answered `[]` on a hub that had billed real calls — invisibly,
106
+ * because the unit suite's in-memory port hands back exactly the object that
107
+ * was written, nulls and all absent. The fake supplied the shape production
108
+ * never sees.
109
+ */
110
+ function withoutNulls(data) {
111
+ const out = {};
112
+ for (const [key, value] of Object.entries(data)) if (value !== null) out[key] = value;
113
+ return out;
114
+ }
98
115
  var UsageStore = class {
99
116
  store;
100
117
  now;
101
- constructor(store, now = Date.now) {
118
+ logger;
119
+ constructor(store, now = Date.now, logger) {
102
120
  this.store = store;
103
121
  this.now = now;
122
+ this.logger = logger;
104
123
  }
105
124
  async init() {
106
125
  await this.store.declareCollection(USAGE_COLLECTION, USAGE_COLUMNS);
@@ -115,10 +134,22 @@ var UsageStore = class {
115
134
  async validRows() {
116
135
  const rows = await this.store.query(USAGE_COLLECTION);
117
136
  const out = [];
137
+ let dropped = 0;
138
+ let firstError = "";
118
139
  for (const row of rows) {
119
- const parsed = UsageRowSchema.safeParse(row.data);
120
- if (parsed.success) out.push(parsed.data);
140
+ const parsed = UsageRowSchema.safeParse(withoutNulls(row.data));
141
+ if (parsed.success) {
142
+ out.push(parsed.data);
143
+ continue;
144
+ }
145
+ dropped += 1;
146
+ if (firstError === "") firstError = parsed.error.issues[0]?.message ?? "unparseable";
121
147
  }
148
+ if (dropped > 0) this.logger?.warn("usage rollup discarded unparseable rows", { meta: {
149
+ dropped,
150
+ kept: out.length,
151
+ firstError
152
+ } });
122
153
  return out;
123
154
  }
124
155
  async rollup(filter) {
@@ -94,12 +94,31 @@ var UsageRowSchema = require_addon.object({
94
94
  function dayKey(ts) {
95
95
  return new Date(ts).toISOString().slice(0, 10);
96
96
  }
97
+ /**
98
+ * SQL `NULL` is how a relational store spells "this row has no value here" —
99
+ * an absent KEY is how Zod's `.optional()` spells it. They are not the same
100
+ * word, and every usage row uses both: an OK row has `errorCode: null`, an
101
+ * error row has `profileId/kind/model: null`.
102
+ *
103
+ * Without this, `UsageRowSchema.safeParse` rejected EVERY row ever written and
104
+ * `llm.getUsage` answered `[]` on a hub that had billed real calls — invisibly,
105
+ * because the unit suite's in-memory port hands back exactly the object that
106
+ * was written, nulls and all absent. The fake supplied the shape production
107
+ * never sees.
108
+ */
109
+ function withoutNulls(data) {
110
+ const out = {};
111
+ for (const [key, value] of Object.entries(data)) if (value !== null) out[key] = value;
112
+ return out;
113
+ }
97
114
  var UsageStore = class {
98
115
  store;
99
116
  now;
100
- constructor(store, now = Date.now) {
117
+ logger;
118
+ constructor(store, now = Date.now, logger) {
101
119
  this.store = store;
102
120
  this.now = now;
121
+ this.logger = logger;
103
122
  }
104
123
  async init() {
105
124
  await this.store.declareCollection(USAGE_COLLECTION, USAGE_COLUMNS);
@@ -114,10 +133,22 @@ var UsageStore = class {
114
133
  async validRows() {
115
134
  const rows = await this.store.query(USAGE_COLLECTION);
116
135
  const out = [];
136
+ let dropped = 0;
137
+ let firstError = "";
117
138
  for (const row of rows) {
118
- const parsed = UsageRowSchema.safeParse(row.data);
119
- if (parsed.success) out.push(parsed.data);
139
+ const parsed = UsageRowSchema.safeParse(withoutNulls(row.data));
140
+ if (parsed.success) {
141
+ out.push(parsed.data);
142
+ continue;
143
+ }
144
+ dropped += 1;
145
+ if (firstError === "") firstError = parsed.error.issues[0]?.message ?? "unparseable";
120
146
  }
147
+ if (dropped > 0) this.logger?.warn("usage rollup discarded unparseable rows", { meta: {
148
+ dropped,
149
+ kept: out.length,
150
+ firstError
151
+ } });
121
152
  return out;
122
153
  }
123
154
  async rollup(filter) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@camstack/addon-ai",
3
- "version": "0.2.17",
3
+ "version": "0.2.18",
4
4
  "description": "AI addon for CamStack — the `llm` collection provider (cloud, LAN, and camstack-managed local llama.cpp profiles) plus the per-node `llm-runtime` managed executor.",
5
5
  "keywords": [
6
6
  "camstack",