@hsuite/smart-engines-sdk 3.11.0 → 3.13.0

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/CHANGELOG.md CHANGED
@@ -252,7 +252,7 @@ class MyService {
252
252
  ValidatorRules` section, two new rows in the "Available clients" table,
253
253
  and a pointer to the full developer guide.
254
254
  - Complete developer guide added at
255
- `docs/developers/libs/rules-engine.md#smart-app-sdk-guide` — covers
255
+ `docs/internal/developers/libs/rules-engine.md#smart-app-sdk-guide` — covers
256
256
  authoring paths, composer reference tables, publish + entity-create
257
257
  end-to-end flow, `baas.rules.*` + `baas.entities.*` reference, and 7
258
258
  common pitfalls.
package/README.md CHANGED
@@ -235,7 +235,7 @@ const sim = await baas.rules.simulate({ ruleRef, action: 'mint', context: { amou
235
235
  // sim.isValid === true | false (+ reason)
236
236
  ```
237
237
 
238
- Full reference: [Smart-App SDK Guide](../../docs/developers/libs/rules-engine.md#smart-app-sdk-guide)
238
+ Full reference: [Smart-App SDK Guide](../../docs/internal/developers/libs/rules-engine.md#smart-app-sdk-guide)
239
239
 
240
240
  ---
241
241
 
package/dist/index.d.ts CHANGED
@@ -3885,7 +3885,7 @@ export type AgentExecuteRequest = {
3885
3885
  params: Record<string, unknown>;
3886
3886
  };
3887
3887
  export type AgentExecuteStepResult = {
3888
- kind: "onchain" | "offchain";
3888
+ kind: "onchain" | "offchain" | "function";
3889
3889
  status: string;
3890
3890
  transactionBytes?: string;
3891
3891
  ref?: string;
@@ -5396,6 +5396,62 @@ declare function xlmToStroops(xlm: string | number): string;
5396
5396
  declare function validateBitcoinAddress(address: string): boolean;
5397
5397
  declare function satoshisToBtc(satoshis: string | number): string;
5398
5398
  declare function btcToSatoshis(btc: string | number): string;
5399
+ export type ModelProvider = "anthropic" | "openai" | "gemini";
5400
+ export type ChatMessage = {
5401
+ role: "system" | "user" | "assistant";
5402
+ content: string;
5403
+ };
5404
+ export type InferArgs = {
5405
+ system?: string;
5406
+ messages: ChatMessage[];
5407
+ maxTokens?: number;
5408
+ timeoutMs?: number;
5409
+ signal?: AbortSignal;
5410
+ };
5411
+ export type InferResult = {
5412
+ ok: boolean;
5413
+ text?: string;
5414
+ error?: string;
5415
+ raw?: unknown;
5416
+ };
5417
+ export type IModelProvider = {
5418
+ readonly provider: ModelProvider;
5419
+ infer(args: InferArgs): Promise<InferResult>;
5420
+ };
5421
+ export declare function createAnthropicProvider(opts: {
5422
+ apiKey: string;
5423
+ model: string;
5424
+ }): IModelProvider;
5425
+ export declare function createOpenAiProvider(opts: {
5426
+ apiKey: string;
5427
+ model: string;
5428
+ }): IModelProvider;
5429
+ export declare function createGeminiProvider(opts: {
5430
+ apiKey: string;
5431
+ model: string;
5432
+ }): IModelProvider;
5433
+ export declare function createModelProvider(opts: {
5434
+ provider: ModelProvider;
5435
+ apiKey: string;
5436
+ model: string;
5437
+ }): IModelProvider;
5438
+ export type InferJsonResult<T> = {
5439
+ ok: boolean;
5440
+ value?: T;
5441
+ error?: string;
5442
+ raw?: string;
5443
+ };
5444
+ export declare function inferJson<T = unknown>(opts: {
5445
+ provider: ModelProvider;
5446
+ apiKey: string;
5447
+ model: string;
5448
+ system?: string;
5449
+ input: unknown;
5450
+ maxTokens?: number;
5451
+ timeoutMs?: number;
5452
+ signal?: AbortSignal;
5453
+ parse?: (raw: unknown) => T;
5454
+ }): Promise<InferJsonResult<T>>;
5399
5455
  export type StateRootResponse = {
5400
5456
  appId: string;
5401
5457
  stateRoot: string;
@@ -25558,6 +25614,9 @@ declare namespace subscription {
25558
25614
  declare namespace faucet {
25559
25615
  export { FaucetChallenge, FaucetClient, FaucetDispenseRequest, FaucetDispenseResult, FaucetDispensed, FaucetRateLimited, FaucetStatusResponse, FaucetTrustlineRequired };
25560
25616
  }
25617
+ declare namespace ai {
25618
+ export { ChatMessage, IModelProvider, InferArgs, InferJsonResult, InferResult, ModelProvider, createAnthropicProvider, createGeminiProvider, createModelProvider, createOpenAiProvider, inferJson };
25619
+ }
25561
25620
  declare namespace bridge {
25562
25621
  export { BridgeChain, BridgeClaimRecord, BridgeClaimStatus, BridgeClient, BridgeConfig, BridgeDestinationConfig, BridgeDirection, BridgeGenesisBinding, BridgeMode, BridgeOperationConfig, BridgeRulesConfig, BridgeSourceConfig, BridgeStatus, BridgeSupply, BridgeTrustLevel, CreateBridgeRequest, CreateBridgeResponse, ListBridgesOptions, ListBridgesResponse, ListClaimsOptions, ListClaimsResponse, PortRequest, PortResult, ReturnRequest };
25563
25622
  }
@@ -25595,6 +25654,7 @@ declare namespace pqcVerify {
25595
25654
  export {
25596
25655
  RetryConfig as ResilienceRetryConfig,
25597
25656
  RetryConfig$1 as RetryConfig,
25657
+ ai,
25598
25658
  auth,
25599
25659
  baas,
25600
25660
  bridge,
package/dist/index.js CHANGED
@@ -10375,6 +10375,358 @@ function btcToSatoshis(btc) {
10375
10375
  return (amount * 1e8).toFixed(0);
10376
10376
  }
10377
10377
 
10378
+ // src/ai/index.ts
10379
+ var ai_exports = {};
10380
+ __export(ai_exports, {
10381
+ createAnthropicProvider: () => createAnthropicProvider,
10382
+ createGeminiProvider: () => createGeminiProvider,
10383
+ createModelProvider: () => createModelProvider,
10384
+ createOpenAiProvider: () => createOpenAiProvider,
10385
+ inferJson: () => inferJson
10386
+ });
10387
+
10388
+ // src/ai/types.ts
10389
+ var DEFAULT_INFER_TIMEOUT_MS = 3e4;
10390
+
10391
+ // src/ai/http.ts
10392
+ var InferTimeoutError = class extends Error {
10393
+ constructor(timeoutMs) {
10394
+ super(`request timed out after ${timeoutMs}ms`);
10395
+ this.name = "InferTimeoutError";
10396
+ }
10397
+ };
10398
+ function isAbortError(err) {
10399
+ if (err instanceof InferTimeoutError) {
10400
+ return true;
10401
+ }
10402
+ return typeof err === "object" && err !== null && err.name === "AbortError";
10403
+ }
10404
+ function errMsg(err) {
10405
+ return err instanceof Error ? err.message : String(err);
10406
+ }
10407
+ async function fetchWithTimeout(url, init, opts = {}) {
10408
+ const timeoutMs = opts.timeoutMs ?? DEFAULT_INFER_TIMEOUT_MS;
10409
+ const callerSignal = opts.signal;
10410
+ const controller = new AbortController();
10411
+ let timedOut = false;
10412
+ let timer;
10413
+ if (timeoutMs > 0) {
10414
+ timer = setTimeout(() => {
10415
+ timedOut = true;
10416
+ controller.abort();
10417
+ }, timeoutMs);
10418
+ if (typeof timer === "object" && typeof timer.unref === "function") {
10419
+ timer.unref();
10420
+ }
10421
+ }
10422
+ const onCallerAbort = () => controller.abort();
10423
+ if (callerSignal) {
10424
+ if (callerSignal.aborted) {
10425
+ controller.abort();
10426
+ } else {
10427
+ callerSignal.addEventListener("abort", onCallerAbort, { once: true });
10428
+ }
10429
+ }
10430
+ try {
10431
+ return await fetch(url, { ...init, signal: controller.signal });
10432
+ } catch (err) {
10433
+ if (timedOut) {
10434
+ throw new InferTimeoutError(timeoutMs);
10435
+ }
10436
+ throw err;
10437
+ } finally {
10438
+ if (timer !== void 0) {
10439
+ clearTimeout(timer);
10440
+ }
10441
+ if (callerSignal) {
10442
+ callerSignal.removeEventListener("abort", onCallerAbort);
10443
+ }
10444
+ }
10445
+ }
10446
+
10447
+ // src/ai/anthropic.ts
10448
+ var ANTHROPIC_URL = "https://api.anthropic.com/v1/messages";
10449
+ var ANTHROPIC_VERSION = "2023-06-01";
10450
+ var DEFAULT_MAX_TOKENS = 1024;
10451
+ function createAnthropicProvider(opts) {
10452
+ const { apiKey, model } = opts;
10453
+ return {
10454
+ provider: "anthropic",
10455
+ async infer(args) {
10456
+ const messages = args.messages.filter((m) => m.role === "user" || m.role === "assistant").map((m) => ({ role: m.role, content: m.content }));
10457
+ const body = {
10458
+ model,
10459
+ max_tokens: args.maxTokens ?? DEFAULT_MAX_TOKENS,
10460
+ messages
10461
+ };
10462
+ if (args.system) {
10463
+ body.system = args.system;
10464
+ }
10465
+ let res;
10466
+ try {
10467
+ res = await fetchWithTimeout(
10468
+ ANTHROPIC_URL,
10469
+ {
10470
+ method: "POST",
10471
+ headers: {
10472
+ "x-api-key": apiKey,
10473
+ "anthropic-version": ANTHROPIC_VERSION,
10474
+ "content-type": "application/json"
10475
+ },
10476
+ body: JSON.stringify(body)
10477
+ },
10478
+ { timeoutMs: args.timeoutMs, signal: args.signal }
10479
+ );
10480
+ } catch (err) {
10481
+ if (isAbortError(err)) {
10482
+ return { ok: false, error: errMsg(err) };
10483
+ }
10484
+ throw err;
10485
+ }
10486
+ if (!res.ok) {
10487
+ throw new Error(`Anthropic request failed with status ${res.status}`);
10488
+ }
10489
+ const data = await res.json();
10490
+ const text = Array.isArray(data.content) ? data.content.find((b) => typeof b.text === "string")?.text : void 0;
10491
+ if (typeof text !== "string") {
10492
+ return { ok: false, error: "Anthropic response contained no text block", raw: data };
10493
+ }
10494
+ return { ok: true, text, raw: data };
10495
+ }
10496
+ };
10497
+ }
10498
+
10499
+ // src/ai/openai.ts
10500
+ var OPENAI_URL = "https://api.openai.com/v1/chat/completions";
10501
+ var DEFAULT_MAX_TOKENS2 = 1024;
10502
+ function createOpenAiProvider(opts) {
10503
+ const { apiKey, model } = opts;
10504
+ return {
10505
+ provider: "openai",
10506
+ async infer(args) {
10507
+ const messages = [];
10508
+ if (args.system) {
10509
+ messages.push({ role: "system", content: args.system });
10510
+ }
10511
+ for (const m of args.messages) {
10512
+ messages.push({ role: m.role, content: m.content });
10513
+ }
10514
+ let res;
10515
+ try {
10516
+ res = await fetchWithTimeout(
10517
+ OPENAI_URL,
10518
+ {
10519
+ method: "POST",
10520
+ headers: {
10521
+ Authorization: `Bearer ${apiKey}`,
10522
+ "content-type": "application/json"
10523
+ },
10524
+ body: JSON.stringify({
10525
+ model,
10526
+ max_tokens: args.maxTokens ?? DEFAULT_MAX_TOKENS2,
10527
+ messages
10528
+ })
10529
+ },
10530
+ { timeoutMs: args.timeoutMs, signal: args.signal }
10531
+ );
10532
+ } catch (err) {
10533
+ if (isAbortError(err)) {
10534
+ return { ok: false, error: errMsg(err) };
10535
+ }
10536
+ throw err;
10537
+ }
10538
+ if (!res.ok) {
10539
+ throw new Error(`OpenAI request failed with status ${res.status}`);
10540
+ }
10541
+ const data = await res.json();
10542
+ const text = data.choices?.[0]?.message?.content;
10543
+ if (typeof text !== "string") {
10544
+ return { ok: false, error: "OpenAI response contained no message content", raw: data };
10545
+ }
10546
+ return { ok: true, text, raw: data };
10547
+ }
10548
+ };
10549
+ }
10550
+
10551
+ // src/ai/gemini.ts
10552
+ var GEMINI_BASE = "https://generativelanguage.googleapis.com/v1beta/models";
10553
+ var DEFAULT_MAX_TOKENS3 = 1024;
10554
+ function createGeminiProvider(opts) {
10555
+ const { apiKey, model } = opts;
10556
+ return {
10557
+ provider: "gemini",
10558
+ async infer(args) {
10559
+ const contents = args.messages.filter((m) => m.role === "user" || m.role === "assistant").map((m) => ({
10560
+ role: m.role === "assistant" ? "model" : "user",
10561
+ parts: [{ text: m.content }]
10562
+ }));
10563
+ const body = {
10564
+ contents,
10565
+ generationConfig: { maxOutputTokens: args.maxTokens ?? DEFAULT_MAX_TOKENS3 }
10566
+ };
10567
+ if (args.system) {
10568
+ body.systemInstruction = { parts: [{ text: args.system }] };
10569
+ }
10570
+ const url = `${GEMINI_BASE}/${model}:generateContent`;
10571
+ let res;
10572
+ try {
10573
+ res = await fetchWithTimeout(
10574
+ url,
10575
+ {
10576
+ method: "POST",
10577
+ headers: {
10578
+ "content-type": "application/json",
10579
+ "x-goog-api-key": apiKey
10580
+ },
10581
+ body: JSON.stringify(body)
10582
+ },
10583
+ { timeoutMs: args.timeoutMs, signal: args.signal }
10584
+ );
10585
+ } catch (err) {
10586
+ if (isAbortError(err)) {
10587
+ return { ok: false, error: errMsg(err) };
10588
+ }
10589
+ throw err;
10590
+ }
10591
+ if (!res.ok) {
10592
+ throw new Error(`Gemini request failed with status ${res.status}`);
10593
+ }
10594
+ const data = await res.json();
10595
+ const text = data.candidates?.[0]?.content?.parts?.[0]?.text;
10596
+ if (typeof text !== "string") {
10597
+ return { ok: false, error: "Gemini response contained no text part", raw: data };
10598
+ }
10599
+ return { ok: true, text, raw: data };
10600
+ }
10601
+ };
10602
+ }
10603
+
10604
+ // src/ai/factory.ts
10605
+ function createModelProvider(opts) {
10606
+ const { provider, apiKey, model } = opts;
10607
+ switch (provider) {
10608
+ case "anthropic":
10609
+ return createAnthropicProvider({ apiKey, model });
10610
+ case "openai":
10611
+ return createOpenAiProvider({ apiKey, model });
10612
+ case "gemini":
10613
+ return createGeminiProvider({ apiKey, model });
10614
+ default: {
10615
+ const never = provider;
10616
+ throw new Error(`Unsupported model provider: ${String(never)}`);
10617
+ }
10618
+ }
10619
+ }
10620
+
10621
+ // src/ai/infer-json.ts
10622
+ var JSON_ONLY_INSTRUCTION = "Respond with ONLY a single JSON object, no prose.";
10623
+ var RETRY_INSTRUCTION = "Return valid JSON only.";
10624
+ function nextBalancedObject(raw, from) {
10625
+ const start = raw.indexOf("{", from);
10626
+ if (start === -1) {
10627
+ return null;
10628
+ }
10629
+ let depth = 0;
10630
+ let inString = false;
10631
+ let escaped = false;
10632
+ for (let i = start; i < raw.length; i++) {
10633
+ const ch = raw[i];
10634
+ if (inString) {
10635
+ if (escaped) {
10636
+ escaped = false;
10637
+ } else if (ch === "\\") {
10638
+ escaped = true;
10639
+ } else if (ch === '"') {
10640
+ inString = false;
10641
+ }
10642
+ continue;
10643
+ }
10644
+ if (ch === '"') {
10645
+ inString = true;
10646
+ } else if (ch === "{") {
10647
+ depth++;
10648
+ } else if (ch === "}") {
10649
+ depth--;
10650
+ if (depth === 0) {
10651
+ return [start, i];
10652
+ }
10653
+ }
10654
+ }
10655
+ return null;
10656
+ }
10657
+ function extractJson(raw) {
10658
+ let from = 0;
10659
+ let sawCandidate = false;
10660
+ for (; ; ) {
10661
+ const span = nextBalancedObject(raw, from);
10662
+ if (!span) {
10663
+ break;
10664
+ }
10665
+ sawCandidate = true;
10666
+ const slice = raw.slice(span[0], span[1] + 1);
10667
+ try {
10668
+ return JSON.parse(slice);
10669
+ } catch {
10670
+ from = span[0] + 1;
10671
+ }
10672
+ }
10673
+ throw new Error(
10674
+ sawCandidate ? "no valid JSON object found in reply" : "no JSON object found in reply"
10675
+ );
10676
+ }
10677
+ async function inferJson(opts) {
10678
+ const { provider, apiKey, model, system, input, maxTokens, timeoutMs, signal, parse } = opts;
10679
+ let client;
10680
+ try {
10681
+ client = createModelProvider({ provider, apiKey, model });
10682
+ } catch (err) {
10683
+ return { ok: false, error: errMsg(err) };
10684
+ }
10685
+ const systemPrompt = system ? `${system}
10686
+
10687
+ ${JSON_ONLY_INSTRUCTION}` : JSON_ONLY_INSTRUCTION;
10688
+ const userContent = typeof input === "string" ? input : JSON.stringify(input);
10689
+ const messages = [{ role: "user", content: userContent }];
10690
+ const first = await runOnce(
10691
+ client,
10692
+ { system: systemPrompt, messages, maxTokens, timeoutMs, signal },
10693
+ parse
10694
+ );
10695
+ if (first.ok) {
10696
+ return first;
10697
+ }
10698
+ const retryMessages = [...messages];
10699
+ if (typeof first.raw === "string") {
10700
+ retryMessages.push({ role: "assistant", content: first.raw });
10701
+ }
10702
+ retryMessages.push({ role: "user", content: RETRY_INSTRUCTION });
10703
+ const second = await runOnce(
10704
+ client,
10705
+ { system: systemPrompt, messages: retryMessages, maxTokens, timeoutMs, signal },
10706
+ parse
10707
+ );
10708
+ return second;
10709
+ }
10710
+ async function runOnce(client, args, parse) {
10711
+ let inferred;
10712
+ try {
10713
+ inferred = await client.infer(args);
10714
+ } catch (err) {
10715
+ return { ok: false, error: errMsg(err) };
10716
+ }
10717
+ if (!inferred.ok || typeof inferred.text !== "string") {
10718
+ return { ok: false, error: inferred.error ?? "provider returned no text", raw: inferred.text };
10719
+ }
10720
+ const raw = inferred.text;
10721
+ try {
10722
+ const parsed = extractJson(raw);
10723
+ const value = parse ? parse(parsed) : parsed;
10724
+ return { ok: true, value, raw };
10725
+ } catch (err) {
10726
+ return { ok: false, error: errMsg(err), raw };
10727
+ }
10728
+ }
10729
+
10378
10730
  // src/baas/index.ts
10379
10731
  var baas_exports = {};
10380
10732
  __export(baas_exports, {
@@ -13501,6 +13853,7 @@ exports.ValidatorDiscoveryClient = ValidatorDiscoveryClient;
13501
13853
  exports.ValidatorMetadataSchema = ValidatorMetadataSchema;
13502
13854
  exports.ValidatorRulesSchema = ValidatorRulesSchema;
13503
13855
  exports.XrplTransactionsClient = XrplTransactionsClient;
13856
+ exports.ai = ai_exports;
13504
13857
  exports.appTopic = appTopic;
13505
13858
  exports.atom = atom;
13506
13859
  exports.auth = auth_exports;
@@ -13509,7 +13862,11 @@ exports.bridge = bridge_exports;
13509
13862
  exports.canonicalizeAttestation = canonicalizeAttestation;
13510
13863
  exports.chains = chains_exports;
13511
13864
  exports.computeAttestationHash = computeAttestationHash;
13865
+ exports.createAnthropicProvider = createAnthropicProvider;
13866
+ exports.createGeminiProvider = createGeminiProvider;
13512
13867
  exports.createHttpClient = createHttpClient;
13868
+ exports.createModelProvider = createModelProvider;
13869
+ exports.createOpenAiProvider = createOpenAiProvider;
13513
13870
  exports.createResilientFetchWithBreaker = createResilientFetchWithBreaker;
13514
13871
  exports.createXrplWeb3Signer = createXrplWeb3Signer;
13515
13872
  exports.dao = dao_exports;
@@ -13525,6 +13882,7 @@ exports.forAgent = forAgent;
13525
13882
  exports.forToken = forToken;
13526
13883
  exports.forTopic = forTopic;
13527
13884
  exports.governance = governance_exports;
13885
+ exports.inferJson = inferJson;
13528
13886
  exports.isKnownNetwork = isKnownNetwork;
13529
13887
  exports.isPersonhoodVerifierNotConfigured = isPersonhoodVerifierNotConfigured;
13530
13888
  exports.isRuleRejected = isRuleRejected;