@syfthub/sdk 0.2.0 → 0.2.1

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.d.cts CHANGED
@@ -909,6 +909,22 @@ interface QueryDataSourceOptions {
909
909
  topK?: number;
910
910
  /** Minimum similarity score (default: 0.5) */
911
911
  similarityThreshold?: number;
912
+ /**
913
+ * Pre-minted satellite token to send as `Authorization: Bearer`. If omitted,
914
+ * one is minted automatically when an owner is known (see `ownerUsername` /
915
+ * `endpoint.ownerUsername`).
916
+ */
917
+ authorizationToken?: string;
918
+ /**
919
+ * Endpoint owner username used as the satellite-token audience. Falls back to
920
+ * `endpoint.ownerUsername`.
921
+ */
922
+ ownerUsername?: string;
923
+ /**
924
+ * If true, settle an MPP `402 Payment Required` challenge via the Hub wallet
925
+ * and retry. If false (default), a `402` throws a `RetrievalError`.
926
+ */
927
+ pay?: boolean;
912
928
  }
913
929
  /**
914
930
  * Options for querying a model directly.
@@ -2785,13 +2801,50 @@ declare class GenerationError extends SyftHubError {
2785
2801
  * For most use cases, prefer the higher-level `client.chat` API instead.
2786
2802
  */
2787
2803
  declare class SyftAIResource {
2804
+ private readonly http;
2805
+ /**
2806
+ * @param http - Hub HTTP client, used to mint satellite tokens and settle
2807
+ * MPP payments. Endpoint queries themselves use direct `fetch`, since the
2808
+ * SyftAI-Space URL is arbitrary and not the Hub base URL.
2809
+ */
2810
+ constructor(http: HTTPClient);
2811
+ /**
2812
+ * Mint a satellite token for `audience` (the endpoint owner's username).
2813
+ *
2814
+ * Mirrors the aggregator's token coordination layer: try an authenticated
2815
+ * token first, then fall back to a guest token. Returns `undefined` if both
2816
+ * fail, so the caller can still attempt an unauthenticated request.
2817
+ */
2818
+ private mintSatelliteToken;
2819
+ /**
2820
+ * Pay an MPP `402` challenge via the Hub wallet, returning an X-Payment credential.
2821
+ *
2822
+ * Mirrors the aggregator's `handleMppPayment`: the `WWW-Authenticate`
2823
+ * challenge is forwarded verbatim to the Hub's `/api/v1/wallet/pay`, which
2824
+ * parses it and returns an `x_payment` string to attach to a retry.
2825
+ */
2826
+ private payMpp;
2788
2827
  /**
2789
2828
  * Build headers for SyftAI-Space request.
2790
2829
  */
2791
2830
  private buildHeaders;
2831
+ /**
2832
+ * Parse documents from a SyftAI-Space query response.
2833
+ *
2834
+ * Mirrors the aggregator's `DataSourceClient._parse_syftai_response`: the
2835
+ * canonical shape nests documents under `references.documents` and names the
2836
+ * score `similarity_score`. A legacy top-level `documents` list (with
2837
+ * `score`) is still honoured for backward compatibility.
2838
+ */
2839
+ private parseDocuments;
2792
2840
  /**
2793
2841
  * Query a data source endpoint directly.
2794
2842
  *
2843
+ * Authentication mirrors the aggregator: SyftAI-Space endpoints expect a
2844
+ * satellite bearer token whose audience is the endpoint owner's username. If
2845
+ * `authorizationToken` is not supplied, one is minted automatically when an
2846
+ * owner is known (`ownerUsername` option or `endpoint.ownerUsername`).
2847
+ *
2795
2848
  * @param options - Query options
2796
2849
  * @returns Array of Document objects
2797
2850
  * @throws {RetrievalError} If the query fails
package/dist/index.d.ts CHANGED
@@ -909,6 +909,22 @@ interface QueryDataSourceOptions {
909
909
  topK?: number;
910
910
  /** Minimum similarity score (default: 0.5) */
911
911
  similarityThreshold?: number;
912
+ /**
913
+ * Pre-minted satellite token to send as `Authorization: Bearer`. If omitted,
914
+ * one is minted automatically when an owner is known (see `ownerUsername` /
915
+ * `endpoint.ownerUsername`).
916
+ */
917
+ authorizationToken?: string;
918
+ /**
919
+ * Endpoint owner username used as the satellite-token audience. Falls back to
920
+ * `endpoint.ownerUsername`.
921
+ */
922
+ ownerUsername?: string;
923
+ /**
924
+ * If true, settle an MPP `402 Payment Required` challenge via the Hub wallet
925
+ * and retry. If false (default), a `402` throws a `RetrievalError`.
926
+ */
927
+ pay?: boolean;
912
928
  }
913
929
  /**
914
930
  * Options for querying a model directly.
@@ -2785,13 +2801,50 @@ declare class GenerationError extends SyftHubError {
2785
2801
  * For most use cases, prefer the higher-level `client.chat` API instead.
2786
2802
  */
2787
2803
  declare class SyftAIResource {
2804
+ private readonly http;
2805
+ /**
2806
+ * @param http - Hub HTTP client, used to mint satellite tokens and settle
2807
+ * MPP payments. Endpoint queries themselves use direct `fetch`, since the
2808
+ * SyftAI-Space URL is arbitrary and not the Hub base URL.
2809
+ */
2810
+ constructor(http: HTTPClient);
2811
+ /**
2812
+ * Mint a satellite token for `audience` (the endpoint owner's username).
2813
+ *
2814
+ * Mirrors the aggregator's token coordination layer: try an authenticated
2815
+ * token first, then fall back to a guest token. Returns `undefined` if both
2816
+ * fail, so the caller can still attempt an unauthenticated request.
2817
+ */
2818
+ private mintSatelliteToken;
2819
+ /**
2820
+ * Pay an MPP `402` challenge via the Hub wallet, returning an X-Payment credential.
2821
+ *
2822
+ * Mirrors the aggregator's `handleMppPayment`: the `WWW-Authenticate`
2823
+ * challenge is forwarded verbatim to the Hub's `/api/v1/wallet/pay`, which
2824
+ * parses it and returns an `x_payment` string to attach to a retry.
2825
+ */
2826
+ private payMpp;
2788
2827
  /**
2789
2828
  * Build headers for SyftAI-Space request.
2790
2829
  */
2791
2830
  private buildHeaders;
2831
+ /**
2832
+ * Parse documents from a SyftAI-Space query response.
2833
+ *
2834
+ * Mirrors the aggregator's `DataSourceClient._parse_syftai_response`: the
2835
+ * canonical shape nests documents under `references.documents` and names the
2836
+ * score `similarity_score`. A legacy top-level `documents` list (with
2837
+ * `score`) is still honoured for backward compatibility.
2838
+ */
2839
+ private parseDocuments;
2792
2840
  /**
2793
2841
  * Query a data source endpoint directly.
2794
2842
  *
2843
+ * Authentication mirrors the aggregator: SyftAI-Space endpoints expect a
2844
+ * satellite bearer token whose audience is the endpoint owner's username. If
2845
+ * `authorizationToken` is not supplied, one is minted automatically when an
2846
+ * owner is known (`ownerUsername` option or `endpoint.ownerUsername`).
2847
+ *
2795
2848
  * @param options - Query options
2796
2849
  * @returns Array of Document objects
2797
2850
  * @throws {RetrievalError} If the query fails
package/dist/index.js CHANGED
@@ -2767,28 +2767,125 @@ var GenerationError = class extends SyftHubError {
2767
2767
  }
2768
2768
  };
2769
2769
  var SyftAIResource = class {
2770
- // No dependencies - uses direct fetch to SyftAI-Space endpoints
2770
+ /**
2771
+ * @param http - Hub HTTP client, used to mint satellite tokens and settle
2772
+ * MPP payments. Endpoint queries themselves use direct `fetch`, since the
2773
+ * SyftAI-Space URL is arbitrary and not the Hub base URL.
2774
+ */
2775
+ constructor(http) {
2776
+ this.http = http;
2777
+ }
2778
+ /**
2779
+ * Mint a satellite token for `audience` (the endpoint owner's username).
2780
+ *
2781
+ * Mirrors the aggregator's token coordination layer: try an authenticated
2782
+ * token first, then fall back to a guest token. Returns `undefined` if both
2783
+ * fail, so the caller can still attempt an unauthenticated request.
2784
+ */
2785
+ async mintSatelliteToken(audience) {
2786
+ if (this.http.hasTokens()) {
2787
+ try {
2788
+ const res = await this.http.get("/api/v1/token", {
2789
+ aud: audience
2790
+ });
2791
+ if (res.targetToken) return res.targetToken;
2792
+ } catch {
2793
+ }
2794
+ }
2795
+ try {
2796
+ const res = await this.http.get(
2797
+ "/api/v1/token/guest",
2798
+ { aud: audience },
2799
+ { includeAuth: false }
2800
+ );
2801
+ return res.targetToken;
2802
+ } catch {
2803
+ return void 0;
2804
+ }
2805
+ }
2806
+ /**
2807
+ * Pay an MPP `402` challenge via the Hub wallet, returning an X-Payment credential.
2808
+ *
2809
+ * Mirrors the aggregator's `handleMppPayment`: the `WWW-Authenticate`
2810
+ * challenge is forwarded verbatim to the Hub's `/api/v1/wallet/pay`, which
2811
+ * parses it and returns an `x_payment` string to attach to a retry.
2812
+ */
2813
+ async payMpp(wwwAuthenticate, slug) {
2814
+ if (!wwwAuthenticate) return void 0;
2815
+ const res = await this.http.post("/api/v1/wallet/pay", {
2816
+ wwwAuthenticate,
2817
+ endpointSlug: slug
2818
+ });
2819
+ return res.xPayment;
2820
+ }
2771
2821
  /**
2772
2822
  * Build headers for SyftAI-Space request.
2773
2823
  */
2774
- buildHeaders(tenantName) {
2824
+ buildHeaders(tenantName, authorizationToken) {
2775
2825
  const headers = {
2776
2826
  "Content-Type": "application/json"
2777
2827
  };
2778
2828
  if (tenantName) {
2779
2829
  headers["X-Tenant-Name"] = tenantName;
2780
2830
  }
2831
+ if (authorizationToken) {
2832
+ headers["Authorization"] = `Bearer ${authorizationToken}`;
2833
+ }
2781
2834
  return headers;
2782
2835
  }
2836
+ /**
2837
+ * Parse documents from a SyftAI-Space query response.
2838
+ *
2839
+ * Mirrors the aggregator's `DataSourceClient._parse_syftai_response`: the
2840
+ * canonical shape nests documents under `references.documents` and names the
2841
+ * score `similarity_score`. A legacy top-level `documents` list (with
2842
+ * `score`) is still honoured for backward compatibility.
2843
+ */
2844
+ parseDocuments(data) {
2845
+ const references = data["references"];
2846
+ let rawDocs;
2847
+ let scoreKey = "score";
2848
+ if (references && typeof references === "object") {
2849
+ rawDocs = references["documents"];
2850
+ scoreKey = "similarity_score";
2851
+ } else {
2852
+ rawDocs = data["documents"];
2853
+ }
2854
+ const documents = [];
2855
+ if (Array.isArray(rawDocs)) {
2856
+ for (const doc of rawDocs) {
2857
+ documents.push({
2858
+ content: String(doc["content"] ?? ""),
2859
+ score: Number(doc[scoreKey] ?? doc["score"] ?? 0),
2860
+ metadata: doc["metadata"] ?? {}
2861
+ });
2862
+ }
2863
+ }
2864
+ return documents;
2865
+ }
2783
2866
  /**
2784
2867
  * Query a data source endpoint directly.
2785
2868
  *
2869
+ * Authentication mirrors the aggregator: SyftAI-Space endpoints expect a
2870
+ * satellite bearer token whose audience is the endpoint owner's username. If
2871
+ * `authorizationToken` is not supplied, one is minted automatically when an
2872
+ * owner is known (`ownerUsername` option or `endpoint.ownerUsername`).
2873
+ *
2786
2874
  * @param options - Query options
2787
2875
  * @returns Array of Document objects
2788
2876
  * @throws {RetrievalError} If the query fails
2789
2877
  */
2790
2878
  async queryDataSource(options) {
2791
- const { endpoint, query, userEmail, topK = 5, similarityThreshold = 0.5 } = options;
2879
+ const {
2880
+ endpoint,
2881
+ query,
2882
+ userEmail,
2883
+ topK = 5,
2884
+ similarityThreshold = 0.5,
2885
+ authorizationToken,
2886
+ ownerUsername,
2887
+ pay = false
2888
+ } = options;
2792
2889
  const url = `${endpoint.url.replace(/\/$/, "")}/api/v1/endpoints/${endpoint.slug}/query`;
2793
2890
  const requestBody = {
2794
2891
  user_email: userEmail,
@@ -2797,19 +2894,44 @@ var SyftAIResource = class {
2797
2894
  limit: topK,
2798
2895
  similarity_threshold: similarityThreshold
2799
2896
  };
2800
- let response;
2801
- try {
2802
- response = await fetch(url, {
2803
- method: "POST",
2804
- headers: this.buildHeaders(endpoint.tenantName),
2805
- body: JSON.stringify(requestBody)
2806
- });
2807
- } catch (error) {
2808
- throw new RetrievalError(
2809
- `Failed to connect to data source '${endpoint.slug}': ${error instanceof Error ? error.message : String(error)}`,
2810
- endpoint.slug,
2811
- error
2812
- );
2897
+ let token = authorizationToken;
2898
+ if (!token) {
2899
+ const audience = ownerUsername ?? endpoint.ownerUsername;
2900
+ if (audience) {
2901
+ token = await this.mintSatelliteToken(audience);
2902
+ }
2903
+ }
2904
+ const headers = this.buildHeaders(endpoint.tenantName, token);
2905
+ const postQuery = async (extraHeaders) => {
2906
+ try {
2907
+ return await fetch(url, {
2908
+ method: "POST",
2909
+ headers: { ...headers, ...extraHeaders },
2910
+ body: JSON.stringify(requestBody)
2911
+ });
2912
+ } catch (error) {
2913
+ throw new RetrievalError(
2914
+ `Failed to connect to data source '${endpoint.slug}': ${error instanceof Error ? error.message : String(error)}`,
2915
+ endpoint.slug,
2916
+ error
2917
+ );
2918
+ }
2919
+ };
2920
+ let response = await postQuery();
2921
+ if (response.status === 402 && pay) {
2922
+ let xPayment;
2923
+ try {
2924
+ xPayment = await this.payMpp(response.headers.get("www-authenticate") ?? "", endpoint.slug);
2925
+ } catch (error) {
2926
+ throw new RetrievalError(
2927
+ `Payment failed for data source '${endpoint.slug}': ${error instanceof Error ? error.message : String(error)}`,
2928
+ endpoint.slug,
2929
+ error
2930
+ );
2931
+ }
2932
+ if (xPayment) {
2933
+ response = await postQuery({ "X-Payment": xPayment });
2934
+ }
2813
2935
  }
2814
2936
  if (!response.ok) {
2815
2937
  let message = `HTTP ${response.status}`;
@@ -2821,17 +2943,7 @@ var SyftAIResource = class {
2821
2943
  throw new RetrievalError(`Data source query failed: ${message}`, endpoint.slug);
2822
2944
  }
2823
2945
  const data = await response.json();
2824
- const documents = [];
2825
- const docsData = data["documents"];
2826
- if (Array.isArray(docsData)) {
2827
- for (const doc of docsData) {
2828
- documents.push({
2829
- content: String(doc["content"] ?? ""),
2830
- score: Number(doc["score"] ?? 0),
2831
- metadata: doc["metadata"] ?? {}
2832
- });
2833
- }
2834
- }
2946
+ const documents = this.parseDocuments(data);
2835
2947
  return documents;
2836
2948
  }
2837
2949
  /**
@@ -3184,7 +3296,7 @@ var SyftHubClient = class {
3184
3296
  */
3185
3297
  get syftai() {
3186
3298
  if (!this._syftai) {
3187
- this._syftai = new SyftAIResource();
3299
+ this._syftai = new SyftAIResource(this.http);
3188
3300
  }
3189
3301
  return this._syftai;
3190
3302
  }