@specstream/langchain 0.0.0-stage → 0.1.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/LICENSE +21 -0
- package/README.md +34 -2
- package/dist/cjs/index.d.ts +6 -0
- package/dist/cjs/index.js +10 -0
- package/dist/cjs/package.json +1 -0
- package/dist/cjs/specstream-extract.d.ts +61 -0
- package/dist/cjs/specstream-extract.js +62 -0
- package/dist/cjs/specstream-search.d.ts +55 -0
- package/dist/cjs/specstream-search.js +55 -0
- package/dist/cjs/utils.d.ts +90 -0
- package/dist/cjs/utils.js +69 -0
- package/dist/index.d.ts +6 -0
- package/dist/index.js +3 -0
- package/dist/specstream-extract.d.ts +61 -0
- package/dist/specstream-extract.js +58 -0
- package/dist/specstream-search.d.ts +55 -0
- package/dist/specstream-search.js +51 -0
- package/dist/utils.d.ts +90 -0
- package/dist/utils.js +64 -0
- package/package.json +75 -4
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 SpecStream SAS
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
CHANGED
|
@@ -1,3 +1,35 @@
|
|
|
1
|
-
#
|
|
1
|
+
# @specstream/langchain
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
[SpecStream](https://specstream.ai) is the live web data engine for teams and agents. One platform to build, search, extract, enrich, map, and monitor data across the entire web.
|
|
4
|
+
|
|
5
|
+
Ask for any dataset in plain words, and SpecStream builds it from the entire live web: every match found, extracted and enriched into structured records, then monitored for change. It runs through the API and MCP, and inside Sheets, CRMs, Slack, n8n, Zapier and the other tools teams already use.
|
|
6
|
+
|
|
7
|
+
Source URL and read time on every value.
|
|
8
|
+
|
|
9
|
+
Three products:
|
|
10
|
+
|
|
11
|
+
1. Search API: `GET /v1/web-search`, $5 per 1,000 searches. The most accurate web search API in the world (scores and method: https://specstream.ai/benchmarks).
|
|
12
|
+
2. Extract API: `POST /v1/extract`, up to 20 URLs a call (web pages, PDF, Word, Excel), $0.80 per 1,000 pages. A page with no text is not charged.
|
|
13
|
+
3. Deep Search: Ask in plain words. Get every match on the live web, in structured rows. Priced per row.
|
|
14
|
+
|
|
15
|
+
## Install
|
|
16
|
+
|
|
17
|
+
```bash
|
|
18
|
+
npm install @specstream/langchain @langchain/core
|
|
19
|
+
export SPECSTREAM_API_KEY=ss_live_... # Agents & API in your SpecStream workspace
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
A free account comes with $10 of credit to use within 30 days and needs no card: [specstream.ai](https://specstream.ai/signup).
|
|
23
|
+
|
|
24
|
+
## Tools
|
|
25
|
+
|
|
26
|
+
Both tools are LangChain `StructuredTool` classes, built like `@langchain/tavily`.
|
|
27
|
+
|
|
28
|
+
| Tool | Name | What it does | Cost |
|
|
29
|
+
| --- | --- | --- | --- |
|
|
30
|
+
| `SpecStreamSearch` | `specstream_search` | Calls `GET /v1/web-search` and returns the answer, matching rows and ranked sources, with page passages when `passages: true` | $5 per 1,000 searches |
|
|
31
|
+
| `SpecStreamExtract` | `specstream_extract` | Calls `POST /v1/extract` and returns the main text, title and published date of 1 to 20 web pages, PDF, Word or Excel files | $0.80 per 1,000 pages (a page with no text is free) |
|
|
32
|
+
|
|
33
|
+
Each tool takes `specstreamApiKey` (default: the `SPECSTREAM_API_KEY` environment variable), `apiBaseUrl`, `name` and `description`. A failed call returns `{ error }` to the model.
|
|
34
|
+
|
|
35
|
+
MIT licensed.
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
export type { SpecStreamSearchAPIRetrieverFields } from "./specstream-search.js";
|
|
2
|
+
export { SpecStreamSearch } from "./specstream-search.js";
|
|
3
|
+
export type { SpecStreamExtractAPIRetrieverFields } from "./specstream-extract.js";
|
|
4
|
+
export { SpecStreamExtract } from "./specstream-extract.js";
|
|
5
|
+
export type { SpecStreamAPIWrapperParams, SpecStreamExtractParams, SpecStreamExtractResponse, SpecStreamExtractResult, SpecStreamSearchParams, SpecStreamSearchResponse, SpecStreamSource, } from "./utils.js";
|
|
6
|
+
export { SpecStreamSearchAPIWrapper, SpecStreamExtractAPIWrapper } from "./utils.js";
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.SpecStreamExtractAPIWrapper = exports.SpecStreamSearchAPIWrapper = exports.SpecStreamExtract = exports.SpecStreamSearch = void 0;
|
|
4
|
+
var specstream_search_js_1 = require("./specstream-search.js");
|
|
5
|
+
Object.defineProperty(exports, "SpecStreamSearch", { enumerable: true, get: function () { return specstream_search_js_1.SpecStreamSearch; } });
|
|
6
|
+
var specstream_extract_js_1 = require("./specstream-extract.js");
|
|
7
|
+
Object.defineProperty(exports, "SpecStreamExtract", { enumerable: true, get: function () { return specstream_extract_js_1.SpecStreamExtract; } });
|
|
8
|
+
var utils_js_1 = require("./utils.js");
|
|
9
|
+
Object.defineProperty(exports, "SpecStreamSearchAPIWrapper", { enumerable: true, get: function () { return utils_js_1.SpecStreamSearchAPIWrapper; } });
|
|
10
|
+
Object.defineProperty(exports, "SpecStreamExtractAPIWrapper", { enumerable: true, get: function () { return utils_js_1.SpecStreamExtractAPIWrapper; } });
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"type":"commonjs"}
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
import { z } from "zod/v3";
|
|
2
|
+
import { CallbackManagerForToolRun } from "@langchain/core/callbacks/manager";
|
|
3
|
+
import { StructuredTool, ToolParams } from "@langchain/core/tools";
|
|
4
|
+
import { InferInteropZodOutput } from "@langchain/core/utils/types";
|
|
5
|
+
import { SpecStreamExtractAPIWrapper, type SpecStreamExtractResponse } from "./utils.js";
|
|
6
|
+
/**
|
|
7
|
+
* Options for the SpecStreamExtract tool.
|
|
8
|
+
*/
|
|
9
|
+
export type SpecStreamExtractAPIRetrieverFields = ToolParams & {
|
|
10
|
+
/** The base URL of the SpecStream API. @default "https://api.specstream.ai" */
|
|
11
|
+
apiBaseUrl?: string;
|
|
12
|
+
/** The API key. Defaults to the `SPECSTREAM_API_KEY` environment variable. */
|
|
13
|
+
specstreamApiKey?: string;
|
|
14
|
+
/** Output format of the page text. Takes precedence over the call parameter. */
|
|
15
|
+
format?: "markdown" | "text";
|
|
16
|
+
/** Replace the tool name. */
|
|
17
|
+
name?: string;
|
|
18
|
+
/** Replace the tool description. */
|
|
19
|
+
description?: string;
|
|
20
|
+
/** Use a custom API wrapper, for example in tests. */
|
|
21
|
+
apiWrapper?: SpecStreamExtractAPIWrapper;
|
|
22
|
+
};
|
|
23
|
+
declare const inputSchema: z.ZodObject<{
|
|
24
|
+
urls: z.ZodArray<z.ZodString, "many">;
|
|
25
|
+
format: z.ZodOptional<z.ZodEnum<["markdown", "text"]>>;
|
|
26
|
+
}, "strip", z.ZodTypeAny, {
|
|
27
|
+
urls: string[];
|
|
28
|
+
format?: "markdown" | "text" | undefined;
|
|
29
|
+
}, {
|
|
30
|
+
urls: string[];
|
|
31
|
+
format?: "markdown" | "text" | undefined;
|
|
32
|
+
}>;
|
|
33
|
+
/**
|
|
34
|
+
* A tool that reads the main text of web pages and documents live with the
|
|
35
|
+
* SpecStream Extract API.
|
|
36
|
+
*
|
|
37
|
+
* Authentication is handled via an API key, passed at construction or set as
|
|
38
|
+
* the environment variable `SPECSTREAM_API_KEY`.
|
|
39
|
+
*/
|
|
40
|
+
export declare class SpecStreamExtract extends StructuredTool<typeof inputSchema> {
|
|
41
|
+
static lc_name(): string;
|
|
42
|
+
description: string;
|
|
43
|
+
name: string;
|
|
44
|
+
schema: z.ZodObject<{
|
|
45
|
+
urls: z.ZodArray<z.ZodString, "many">;
|
|
46
|
+
format: z.ZodOptional<z.ZodEnum<["markdown", "text"]>>;
|
|
47
|
+
}, "strip", z.ZodTypeAny, {
|
|
48
|
+
urls: string[];
|
|
49
|
+
format?: "markdown" | "text" | undefined;
|
|
50
|
+
}, {
|
|
51
|
+
urls: string[];
|
|
52
|
+
format?: "markdown" | "text" | undefined;
|
|
53
|
+
}>;
|
|
54
|
+
format?: "markdown" | "text";
|
|
55
|
+
private apiWrapper;
|
|
56
|
+
constructor(params?: SpecStreamExtractAPIRetrieverFields);
|
|
57
|
+
_call(input: InferInteropZodOutput<typeof inputSchema>, _runManager?: CallbackManagerForToolRun): Promise<SpecStreamExtractResponse | {
|
|
58
|
+
error: string;
|
|
59
|
+
}>;
|
|
60
|
+
}
|
|
61
|
+
export {};
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.SpecStreamExtract = void 0;
|
|
4
|
+
const v3_1 = require("zod/v3");
|
|
5
|
+
const tools_1 = require("@langchain/core/tools");
|
|
6
|
+
const utils_js_1 = require("./utils.js");
|
|
7
|
+
const inputSchema = v3_1.z.object({
|
|
8
|
+
urls: v3_1.z
|
|
9
|
+
.array(v3_1.z.string().min(1))
|
|
10
|
+
.min(1)
|
|
11
|
+
.max(20)
|
|
12
|
+
.describe("1 to 20 http or https URLs of web pages, PDF, Word or Excel files to read."),
|
|
13
|
+
format: v3_1.z
|
|
14
|
+
.enum(["markdown", "text"])
|
|
15
|
+
.optional()
|
|
16
|
+
.describe('The format of the page text: "markdown" (default) keeps headings, lists and links; "text" is plain text.'),
|
|
17
|
+
});
|
|
18
|
+
/**
|
|
19
|
+
* A tool that reads the main text of web pages and documents live with the
|
|
20
|
+
* SpecStream Extract API.
|
|
21
|
+
*
|
|
22
|
+
* Authentication is handled via an API key, passed at construction or set as
|
|
23
|
+
* the environment variable `SPECSTREAM_API_KEY`.
|
|
24
|
+
*/
|
|
25
|
+
class SpecStreamExtract extends tools_1.StructuredTool {
|
|
26
|
+
static lc_name() {
|
|
27
|
+
return "SpecStreamExtract";
|
|
28
|
+
}
|
|
29
|
+
description = "Read the main text, title and published date of up to 20 web pages, PDF, Word or Excel files live with the SpecStream Extract API. Input should be a list of URLs.";
|
|
30
|
+
name = "specstream_extract";
|
|
31
|
+
schema = inputSchema;
|
|
32
|
+
format;
|
|
33
|
+
apiWrapper;
|
|
34
|
+
constructor(params = {}) {
|
|
35
|
+
super(params);
|
|
36
|
+
if (params.name) {
|
|
37
|
+
this.name = params.name;
|
|
38
|
+
}
|
|
39
|
+
if (params.description) {
|
|
40
|
+
this.description = params.description;
|
|
41
|
+
}
|
|
42
|
+
this.format = params.format;
|
|
43
|
+
this.apiWrapper =
|
|
44
|
+
params.apiWrapper ??
|
|
45
|
+
new utils_js_1.SpecStreamExtractAPIWrapper({
|
|
46
|
+
specstreamApiKey: params.specstreamApiKey,
|
|
47
|
+
apiBaseUrl: params.apiBaseUrl,
|
|
48
|
+
});
|
|
49
|
+
}
|
|
50
|
+
async _call(input, _runManager) {
|
|
51
|
+
try {
|
|
52
|
+
return await this.apiWrapper.rawResults({
|
|
53
|
+
urls: input.urls,
|
|
54
|
+
format: this.format ?? input.format,
|
|
55
|
+
});
|
|
56
|
+
}
|
|
57
|
+
catch (e) {
|
|
58
|
+
return { error: e instanceof Error ? e.message : String(e) };
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
exports.SpecStreamExtract = SpecStreamExtract;
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import { z } from "zod/v3";
|
|
2
|
+
import { CallbackManagerForToolRun } from "@langchain/core/callbacks/manager";
|
|
3
|
+
import { StructuredTool, ToolParams } from "@langchain/core/tools";
|
|
4
|
+
import { InferInteropZodOutput } from "@langchain/core/utils/types";
|
|
5
|
+
import { SpecStreamSearchAPIWrapper, type SpecStreamSearchResponse } from "./utils.js";
|
|
6
|
+
/**
|
|
7
|
+
* Options for the SpecStreamSearch tool.
|
|
8
|
+
*/
|
|
9
|
+
export type SpecStreamSearchAPIRetrieverFields = ToolParams & {
|
|
10
|
+
/** The base URL of the SpecStream API. @default "https://api.specstream.ai" */
|
|
11
|
+
apiBaseUrl?: string;
|
|
12
|
+
/** The API key. Defaults to the `SPECSTREAM_API_KEY` environment variable. */
|
|
13
|
+
specstreamApiKey?: string;
|
|
14
|
+
/** Add the text of the top pages to every search. Takes up to about a second more. @default false */
|
|
15
|
+
passages?: boolean;
|
|
16
|
+
/** Replace the tool name. */
|
|
17
|
+
name?: string;
|
|
18
|
+
/** Replace the tool description. */
|
|
19
|
+
description?: string;
|
|
20
|
+
/** Use a custom API wrapper, for example in tests. */
|
|
21
|
+
apiWrapper?: SpecStreamSearchAPIWrapper;
|
|
22
|
+
};
|
|
23
|
+
declare const inputSchema: z.ZodObject<{
|
|
24
|
+
query: z.ZodString;
|
|
25
|
+
}, "strip", z.ZodTypeAny, {
|
|
26
|
+
query: string;
|
|
27
|
+
}, {
|
|
28
|
+
query: string;
|
|
29
|
+
}>;
|
|
30
|
+
/**
|
|
31
|
+
* A tool that searches the live web with the SpecStream Search API and
|
|
32
|
+
* returns the answer, matching rows and ranked sources.
|
|
33
|
+
*
|
|
34
|
+
* Authentication is handled via an API key, passed at construction or set as
|
|
35
|
+
* the environment variable `SPECSTREAM_API_KEY`.
|
|
36
|
+
*/
|
|
37
|
+
export declare class SpecStreamSearch extends StructuredTool<typeof inputSchema> {
|
|
38
|
+
static lc_name(): string;
|
|
39
|
+
description: string;
|
|
40
|
+
name: string;
|
|
41
|
+
schema: z.ZodObject<{
|
|
42
|
+
query: z.ZodString;
|
|
43
|
+
}, "strip", z.ZodTypeAny, {
|
|
44
|
+
query: string;
|
|
45
|
+
}, {
|
|
46
|
+
query: string;
|
|
47
|
+
}>;
|
|
48
|
+
passages?: boolean;
|
|
49
|
+
private apiWrapper;
|
|
50
|
+
constructor(params?: SpecStreamSearchAPIRetrieverFields);
|
|
51
|
+
_call(input: InferInteropZodOutput<typeof inputSchema>, _runManager?: CallbackManagerForToolRun): Promise<SpecStreamSearchResponse | {
|
|
52
|
+
error: string;
|
|
53
|
+
}>;
|
|
54
|
+
}
|
|
55
|
+
export {};
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.SpecStreamSearch = void 0;
|
|
4
|
+
const v3_1 = require("zod/v3");
|
|
5
|
+
const tools_1 = require("@langchain/core/tools");
|
|
6
|
+
const utils_js_1 = require("./utils.js");
|
|
7
|
+
const inputSchema = v3_1.z.object({
|
|
8
|
+
query: v3_1.z
|
|
9
|
+
.string()
|
|
10
|
+
.min(1)
|
|
11
|
+
.max(2000)
|
|
12
|
+
.describe("The question or request in everyday words, 1 to 2,000 characters."),
|
|
13
|
+
});
|
|
14
|
+
/**
|
|
15
|
+
* A tool that searches the live web with the SpecStream Search API and
|
|
16
|
+
* returns the answer, matching rows and ranked sources.
|
|
17
|
+
*
|
|
18
|
+
* Authentication is handled via an API key, passed at construction or set as
|
|
19
|
+
* the environment variable `SPECSTREAM_API_KEY`.
|
|
20
|
+
*/
|
|
21
|
+
class SpecStreamSearch extends tools_1.StructuredTool {
|
|
22
|
+
static lc_name() {
|
|
23
|
+
return "SpecStreamSearch";
|
|
24
|
+
}
|
|
25
|
+
description = "Search the live web with the SpecStream Search API. Returns the answer with the page it was read from, matching rows with their fields, and ranked sources. Input should be a question or request in everyday words.";
|
|
26
|
+
name = "specstream_search";
|
|
27
|
+
schema = inputSchema;
|
|
28
|
+
passages;
|
|
29
|
+
apiWrapper;
|
|
30
|
+
constructor(params = {}) {
|
|
31
|
+
super(params);
|
|
32
|
+
if (params.name) {
|
|
33
|
+
this.name = params.name;
|
|
34
|
+
}
|
|
35
|
+
if (params.description) {
|
|
36
|
+
this.description = params.description;
|
|
37
|
+
}
|
|
38
|
+
this.passages = params.passages;
|
|
39
|
+
this.apiWrapper =
|
|
40
|
+
params.apiWrapper ??
|
|
41
|
+
new utils_js_1.SpecStreamSearchAPIWrapper({
|
|
42
|
+
specstreamApiKey: params.specstreamApiKey,
|
|
43
|
+
apiBaseUrl: params.apiBaseUrl,
|
|
44
|
+
});
|
|
45
|
+
}
|
|
46
|
+
async _call(input, _runManager) {
|
|
47
|
+
try {
|
|
48
|
+
return await this.apiWrapper.rawResults({ q: input.query, passages: this.passages });
|
|
49
|
+
}
|
|
50
|
+
catch (e) {
|
|
51
|
+
return { error: e instanceof Error ? e.message : String(e) };
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
exports.SpecStreamSearch = SpecStreamSearch;
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
export declare const SPECSTREAM_BASE_URL = "https://api.specstream.ai";
|
|
2
|
+
export declare const MAX_EXTRACT_URLS = 20;
|
|
3
|
+
export type SpecStreamSearchParams = {
|
|
4
|
+
/** The question or request in everyday words, 1 to 2,000 characters. */
|
|
5
|
+
q: string;
|
|
6
|
+
/** Add the text of the top pages to the reply. */
|
|
7
|
+
passages?: boolean;
|
|
8
|
+
};
|
|
9
|
+
export type SpecStreamSource = {
|
|
10
|
+
url: string;
|
|
11
|
+
title?: string;
|
|
12
|
+
site?: string;
|
|
13
|
+
snippet?: string;
|
|
14
|
+
published?: string;
|
|
15
|
+
cited?: boolean;
|
|
16
|
+
};
|
|
17
|
+
export type SpecStreamSearchResponse = {
|
|
18
|
+
id?: string;
|
|
19
|
+
request_id?: string;
|
|
20
|
+
kind: "fact" | "list";
|
|
21
|
+
query?: string;
|
|
22
|
+
columns?: unknown[];
|
|
23
|
+
answer: {
|
|
24
|
+
value: string;
|
|
25
|
+
page: string;
|
|
26
|
+
seen_at?: string;
|
|
27
|
+
} | null;
|
|
28
|
+
rows: Array<{
|
|
29
|
+
name?: string;
|
|
30
|
+
page?: string;
|
|
31
|
+
snippet?: string;
|
|
32
|
+
probability?: number;
|
|
33
|
+
sources?: string[];
|
|
34
|
+
fields?: Record<string, unknown>;
|
|
35
|
+
}>;
|
|
36
|
+
sources: SpecStreamSource[];
|
|
37
|
+
passages?: Array<{
|
|
38
|
+
url: string;
|
|
39
|
+
title?: string;
|
|
40
|
+
text: string;
|
|
41
|
+
}>;
|
|
42
|
+
handoff?: Record<string, unknown>;
|
|
43
|
+
took_ms?: number;
|
|
44
|
+
price_usd?: number;
|
|
45
|
+
};
|
|
46
|
+
export type SpecStreamExtractParams = {
|
|
47
|
+
/** 1 to 20 http or https URLs. */
|
|
48
|
+
urls: string[];
|
|
49
|
+
format?: "markdown" | "text";
|
|
50
|
+
};
|
|
51
|
+
export type SpecStreamExtractResult = {
|
|
52
|
+
url: string;
|
|
53
|
+
status: "ok" | "blocked" | "not_found" | "empty" | "unsupported" | "invalid_url" | "failed";
|
|
54
|
+
text?: string;
|
|
55
|
+
title?: string;
|
|
56
|
+
published?: string;
|
|
57
|
+
read_from?: "live" | "site" | "archive";
|
|
58
|
+
captured_at?: string;
|
|
59
|
+
};
|
|
60
|
+
export type SpecStreamExtractResponse = {
|
|
61
|
+
results: SpecStreamExtractResult[];
|
|
62
|
+
request_id?: string;
|
|
63
|
+
took_ms?: number;
|
|
64
|
+
price_usd?: number;
|
|
65
|
+
};
|
|
66
|
+
export type SpecStreamAPIWrapperParams = {
|
|
67
|
+
specstreamApiKey?: string;
|
|
68
|
+
apiBaseUrl?: string;
|
|
69
|
+
};
|
|
70
|
+
declare abstract class BaseSpecStreamAPIWrapper {
|
|
71
|
+
protected specstreamApiKey: string;
|
|
72
|
+
protected apiBaseUrl: string;
|
|
73
|
+
constructor(params?: SpecStreamAPIWrapperParams);
|
|
74
|
+
protected call<T>(method: "GET" | "POST", path: string, body?: Record<string, unknown>): Promise<T>;
|
|
75
|
+
}
|
|
76
|
+
/**
|
|
77
|
+
* A wrapper that encapsulates access to the SpecStream Search API
|
|
78
|
+
* (`GET /v1/web-search`). Primarily used for testing.
|
|
79
|
+
*/
|
|
80
|
+
export declare class SpecStreamSearchAPIWrapper extends BaseSpecStreamAPIWrapper {
|
|
81
|
+
rawResults(params: SpecStreamSearchParams): Promise<SpecStreamSearchResponse>;
|
|
82
|
+
}
|
|
83
|
+
/**
|
|
84
|
+
* A wrapper that encapsulates access to the SpecStream Extract API
|
|
85
|
+
* (`POST /v1/extract`). Primarily used for testing.
|
|
86
|
+
*/
|
|
87
|
+
export declare class SpecStreamExtractAPIWrapper extends BaseSpecStreamAPIWrapper {
|
|
88
|
+
rawResults(params: SpecStreamExtractParams): Promise<SpecStreamExtractResponse>;
|
|
89
|
+
}
|
|
90
|
+
export {};
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.SpecStreamExtractAPIWrapper = exports.SpecStreamSearchAPIWrapper = exports.MAX_EXTRACT_URLS = exports.SPECSTREAM_BASE_URL = void 0;
|
|
4
|
+
const env_1 = require("@langchain/core/utils/env");
|
|
5
|
+
exports.SPECSTREAM_BASE_URL = "https://api.specstream.ai";
|
|
6
|
+
exports.MAX_EXTRACT_URLS = 20;
|
|
7
|
+
class BaseSpecStreamAPIWrapper {
|
|
8
|
+
specstreamApiKey;
|
|
9
|
+
apiBaseUrl;
|
|
10
|
+
constructor(params = {}) {
|
|
11
|
+
const apiKey = (params.specstreamApiKey ?? (0, env_1.getEnvironmentVariable)("SPECSTREAM_API_KEY") ?? "").trim();
|
|
12
|
+
if (!apiKey) {
|
|
13
|
+
throw new Error("No SpecStream API key found. Pass specstreamApiKey or set SPECSTREAM_API_KEY. Create a key under Agents & API in your SpecStream workspace.");
|
|
14
|
+
}
|
|
15
|
+
this.specstreamApiKey = apiKey.replace(/^bearer\s+/i, "");
|
|
16
|
+
this.apiBaseUrl = (params.apiBaseUrl ?? (0, env_1.getEnvironmentVariable)("SPECSTREAM_BASE_URL") ?? exports.SPECSTREAM_BASE_URL).replace(/\/+$/, "");
|
|
17
|
+
}
|
|
18
|
+
async call(method, path, body) {
|
|
19
|
+
const response = await fetch(`${this.apiBaseUrl}${path}`, {
|
|
20
|
+
method,
|
|
21
|
+
headers: {
|
|
22
|
+
Authorization: `Bearer ${this.specstreamApiKey}`,
|
|
23
|
+
Accept: "application/json",
|
|
24
|
+
...(body === undefined ? {} : { "Content-Type": "application/json" }),
|
|
25
|
+
"User-Agent": "@specstream/langchain",
|
|
26
|
+
},
|
|
27
|
+
body: body === undefined ? undefined : JSON.stringify(body),
|
|
28
|
+
});
|
|
29
|
+
let payload = {};
|
|
30
|
+
try {
|
|
31
|
+
payload = await response.json();
|
|
32
|
+
}
|
|
33
|
+
catch {
|
|
34
|
+
// A reply that is not JSON is reported by its status below.
|
|
35
|
+
}
|
|
36
|
+
if (!response.ok || !payload?.data) {
|
|
37
|
+
throw new Error(`Error ${response.status}: ${payload?.error?.message ?? "Unknown error"}`);
|
|
38
|
+
}
|
|
39
|
+
return payload.data;
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* A wrapper that encapsulates access to the SpecStream Search API
|
|
44
|
+
* (`GET /v1/web-search`). Primarily used for testing.
|
|
45
|
+
*/
|
|
46
|
+
class SpecStreamSearchAPIWrapper extends BaseSpecStreamAPIWrapper {
|
|
47
|
+
async rawResults(params) {
|
|
48
|
+
const query = new URLSearchParams({ q: params.q.trim() });
|
|
49
|
+
if (params.passages) {
|
|
50
|
+
query.set("passages", "true");
|
|
51
|
+
}
|
|
52
|
+
return this.call("GET", `/v1/web-search?${query.toString()}`);
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
exports.SpecStreamSearchAPIWrapper = SpecStreamSearchAPIWrapper;
|
|
56
|
+
/**
|
|
57
|
+
* A wrapper that encapsulates access to the SpecStream Extract API
|
|
58
|
+
* (`POST /v1/extract`). Primarily used for testing.
|
|
59
|
+
*/
|
|
60
|
+
class SpecStreamExtractAPIWrapper extends BaseSpecStreamAPIWrapper {
|
|
61
|
+
async rawResults(params) {
|
|
62
|
+
const urls = params.urls.map((url) => url.trim()).filter(Boolean).slice(0, exports.MAX_EXTRACT_URLS);
|
|
63
|
+
return this.call("POST", "/v1/extract", {
|
|
64
|
+
urls,
|
|
65
|
+
format: params.format === "text" ? "text" : "markdown",
|
|
66
|
+
});
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
exports.SpecStreamExtractAPIWrapper = SpecStreamExtractAPIWrapper;
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
export type { SpecStreamSearchAPIRetrieverFields } from "./specstream-search.js";
|
|
2
|
+
export { SpecStreamSearch } from "./specstream-search.js";
|
|
3
|
+
export type { SpecStreamExtractAPIRetrieverFields } from "./specstream-extract.js";
|
|
4
|
+
export { SpecStreamExtract } from "./specstream-extract.js";
|
|
5
|
+
export type { SpecStreamAPIWrapperParams, SpecStreamExtractParams, SpecStreamExtractResponse, SpecStreamExtractResult, SpecStreamSearchParams, SpecStreamSearchResponse, SpecStreamSource, } from "./utils.js";
|
|
6
|
+
export { SpecStreamSearchAPIWrapper, SpecStreamExtractAPIWrapper } from "./utils.js";
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
import { z } from "zod/v3";
|
|
2
|
+
import { CallbackManagerForToolRun } from "@langchain/core/callbacks/manager";
|
|
3
|
+
import { StructuredTool, ToolParams } from "@langchain/core/tools";
|
|
4
|
+
import { InferInteropZodOutput } from "@langchain/core/utils/types";
|
|
5
|
+
import { SpecStreamExtractAPIWrapper, type SpecStreamExtractResponse } from "./utils.js";
|
|
6
|
+
/**
|
|
7
|
+
* Options for the SpecStreamExtract tool.
|
|
8
|
+
*/
|
|
9
|
+
export type SpecStreamExtractAPIRetrieverFields = ToolParams & {
|
|
10
|
+
/** The base URL of the SpecStream API. @default "https://api.specstream.ai" */
|
|
11
|
+
apiBaseUrl?: string;
|
|
12
|
+
/** The API key. Defaults to the `SPECSTREAM_API_KEY` environment variable. */
|
|
13
|
+
specstreamApiKey?: string;
|
|
14
|
+
/** Output format of the page text. Takes precedence over the call parameter. */
|
|
15
|
+
format?: "markdown" | "text";
|
|
16
|
+
/** Replace the tool name. */
|
|
17
|
+
name?: string;
|
|
18
|
+
/** Replace the tool description. */
|
|
19
|
+
description?: string;
|
|
20
|
+
/** Use a custom API wrapper, for example in tests. */
|
|
21
|
+
apiWrapper?: SpecStreamExtractAPIWrapper;
|
|
22
|
+
};
|
|
23
|
+
declare const inputSchema: z.ZodObject<{
|
|
24
|
+
urls: z.ZodArray<z.ZodString, "many">;
|
|
25
|
+
format: z.ZodOptional<z.ZodEnum<["markdown", "text"]>>;
|
|
26
|
+
}, "strip", z.ZodTypeAny, {
|
|
27
|
+
urls: string[];
|
|
28
|
+
format?: "markdown" | "text" | undefined;
|
|
29
|
+
}, {
|
|
30
|
+
urls: string[];
|
|
31
|
+
format?: "markdown" | "text" | undefined;
|
|
32
|
+
}>;
|
|
33
|
+
/**
|
|
34
|
+
* A tool that reads the main text of web pages and documents live with the
|
|
35
|
+
* SpecStream Extract API.
|
|
36
|
+
*
|
|
37
|
+
* Authentication is handled via an API key, passed at construction or set as
|
|
38
|
+
* the environment variable `SPECSTREAM_API_KEY`.
|
|
39
|
+
*/
|
|
40
|
+
export declare class SpecStreamExtract extends StructuredTool<typeof inputSchema> {
|
|
41
|
+
static lc_name(): string;
|
|
42
|
+
description: string;
|
|
43
|
+
name: string;
|
|
44
|
+
schema: z.ZodObject<{
|
|
45
|
+
urls: z.ZodArray<z.ZodString, "many">;
|
|
46
|
+
format: z.ZodOptional<z.ZodEnum<["markdown", "text"]>>;
|
|
47
|
+
}, "strip", z.ZodTypeAny, {
|
|
48
|
+
urls: string[];
|
|
49
|
+
format?: "markdown" | "text" | undefined;
|
|
50
|
+
}, {
|
|
51
|
+
urls: string[];
|
|
52
|
+
format?: "markdown" | "text" | undefined;
|
|
53
|
+
}>;
|
|
54
|
+
format?: "markdown" | "text";
|
|
55
|
+
private apiWrapper;
|
|
56
|
+
constructor(params?: SpecStreamExtractAPIRetrieverFields);
|
|
57
|
+
_call(input: InferInteropZodOutput<typeof inputSchema>, _runManager?: CallbackManagerForToolRun): Promise<SpecStreamExtractResponse | {
|
|
58
|
+
error: string;
|
|
59
|
+
}>;
|
|
60
|
+
}
|
|
61
|
+
export {};
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import { z } from "zod/v3";
|
|
2
|
+
import { StructuredTool } from "@langchain/core/tools";
|
|
3
|
+
import { SpecStreamExtractAPIWrapper } from "./utils.js";
|
|
4
|
+
const inputSchema = z.object({
|
|
5
|
+
urls: z
|
|
6
|
+
.array(z.string().min(1))
|
|
7
|
+
.min(1)
|
|
8
|
+
.max(20)
|
|
9
|
+
.describe("1 to 20 http or https URLs of web pages, PDF, Word or Excel files to read."),
|
|
10
|
+
format: z
|
|
11
|
+
.enum(["markdown", "text"])
|
|
12
|
+
.optional()
|
|
13
|
+
.describe('The format of the page text: "markdown" (default) keeps headings, lists and links; "text" is plain text.'),
|
|
14
|
+
});
|
|
15
|
+
/**
|
|
16
|
+
* A tool that reads the main text of web pages and documents live with the
|
|
17
|
+
* SpecStream Extract API.
|
|
18
|
+
*
|
|
19
|
+
* Authentication is handled via an API key, passed at construction or set as
|
|
20
|
+
* the environment variable `SPECSTREAM_API_KEY`.
|
|
21
|
+
*/
|
|
22
|
+
export class SpecStreamExtract extends StructuredTool {
|
|
23
|
+
static lc_name() {
|
|
24
|
+
return "SpecStreamExtract";
|
|
25
|
+
}
|
|
26
|
+
description = "Read the main text, title and published date of up to 20 web pages, PDF, Word or Excel files live with the SpecStream Extract API. Input should be a list of URLs.";
|
|
27
|
+
name = "specstream_extract";
|
|
28
|
+
schema = inputSchema;
|
|
29
|
+
format;
|
|
30
|
+
apiWrapper;
|
|
31
|
+
constructor(params = {}) {
|
|
32
|
+
super(params);
|
|
33
|
+
if (params.name) {
|
|
34
|
+
this.name = params.name;
|
|
35
|
+
}
|
|
36
|
+
if (params.description) {
|
|
37
|
+
this.description = params.description;
|
|
38
|
+
}
|
|
39
|
+
this.format = params.format;
|
|
40
|
+
this.apiWrapper =
|
|
41
|
+
params.apiWrapper ??
|
|
42
|
+
new SpecStreamExtractAPIWrapper({
|
|
43
|
+
specstreamApiKey: params.specstreamApiKey,
|
|
44
|
+
apiBaseUrl: params.apiBaseUrl,
|
|
45
|
+
});
|
|
46
|
+
}
|
|
47
|
+
async _call(input, _runManager) {
|
|
48
|
+
try {
|
|
49
|
+
return await this.apiWrapper.rawResults({
|
|
50
|
+
urls: input.urls,
|
|
51
|
+
format: this.format ?? input.format,
|
|
52
|
+
});
|
|
53
|
+
}
|
|
54
|
+
catch (e) {
|
|
55
|
+
return { error: e instanceof Error ? e.message : String(e) };
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
}
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import { z } from "zod/v3";
|
|
2
|
+
import { CallbackManagerForToolRun } from "@langchain/core/callbacks/manager";
|
|
3
|
+
import { StructuredTool, ToolParams } from "@langchain/core/tools";
|
|
4
|
+
import { InferInteropZodOutput } from "@langchain/core/utils/types";
|
|
5
|
+
import { SpecStreamSearchAPIWrapper, type SpecStreamSearchResponse } from "./utils.js";
|
|
6
|
+
/**
|
|
7
|
+
* Options for the SpecStreamSearch tool.
|
|
8
|
+
*/
|
|
9
|
+
export type SpecStreamSearchAPIRetrieverFields = ToolParams & {
|
|
10
|
+
/** The base URL of the SpecStream API. @default "https://api.specstream.ai" */
|
|
11
|
+
apiBaseUrl?: string;
|
|
12
|
+
/** The API key. Defaults to the `SPECSTREAM_API_KEY` environment variable. */
|
|
13
|
+
specstreamApiKey?: string;
|
|
14
|
+
/** Add the text of the top pages to every search. Takes up to about a second more. @default false */
|
|
15
|
+
passages?: boolean;
|
|
16
|
+
/** Replace the tool name. */
|
|
17
|
+
name?: string;
|
|
18
|
+
/** Replace the tool description. */
|
|
19
|
+
description?: string;
|
|
20
|
+
/** Use a custom API wrapper, for example in tests. */
|
|
21
|
+
apiWrapper?: SpecStreamSearchAPIWrapper;
|
|
22
|
+
};
|
|
23
|
+
declare const inputSchema: z.ZodObject<{
|
|
24
|
+
query: z.ZodString;
|
|
25
|
+
}, "strip", z.ZodTypeAny, {
|
|
26
|
+
query: string;
|
|
27
|
+
}, {
|
|
28
|
+
query: string;
|
|
29
|
+
}>;
|
|
30
|
+
/**
|
|
31
|
+
* A tool that searches the live web with the SpecStream Search API and
|
|
32
|
+
* returns the answer, matching rows and ranked sources.
|
|
33
|
+
*
|
|
34
|
+
* Authentication is handled via an API key, passed at construction or set as
|
|
35
|
+
* the environment variable `SPECSTREAM_API_KEY`.
|
|
36
|
+
*/
|
|
37
|
+
export declare class SpecStreamSearch extends StructuredTool<typeof inputSchema> {
|
|
38
|
+
static lc_name(): string;
|
|
39
|
+
description: string;
|
|
40
|
+
name: string;
|
|
41
|
+
schema: z.ZodObject<{
|
|
42
|
+
query: z.ZodString;
|
|
43
|
+
}, "strip", z.ZodTypeAny, {
|
|
44
|
+
query: string;
|
|
45
|
+
}, {
|
|
46
|
+
query: string;
|
|
47
|
+
}>;
|
|
48
|
+
passages?: boolean;
|
|
49
|
+
private apiWrapper;
|
|
50
|
+
constructor(params?: SpecStreamSearchAPIRetrieverFields);
|
|
51
|
+
_call(input: InferInteropZodOutput<typeof inputSchema>, _runManager?: CallbackManagerForToolRun): Promise<SpecStreamSearchResponse | {
|
|
52
|
+
error: string;
|
|
53
|
+
}>;
|
|
54
|
+
}
|
|
55
|
+
export {};
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import { z } from "zod/v3";
|
|
2
|
+
import { StructuredTool } from "@langchain/core/tools";
|
|
3
|
+
import { SpecStreamSearchAPIWrapper } from "./utils.js";
|
|
4
|
+
const inputSchema = z.object({
|
|
5
|
+
query: z
|
|
6
|
+
.string()
|
|
7
|
+
.min(1)
|
|
8
|
+
.max(2000)
|
|
9
|
+
.describe("The question or request in everyday words, 1 to 2,000 characters."),
|
|
10
|
+
});
|
|
11
|
+
/**
|
|
12
|
+
* A tool that searches the live web with the SpecStream Search API and
|
|
13
|
+
* returns the answer, matching rows and ranked sources.
|
|
14
|
+
*
|
|
15
|
+
* Authentication is handled via an API key, passed at construction or set as
|
|
16
|
+
* the environment variable `SPECSTREAM_API_KEY`.
|
|
17
|
+
*/
|
|
18
|
+
export class SpecStreamSearch extends StructuredTool {
|
|
19
|
+
static lc_name() {
|
|
20
|
+
return "SpecStreamSearch";
|
|
21
|
+
}
|
|
22
|
+
description = "Search the live web with the SpecStream Search API. Returns the answer with the page it was read from, matching rows with their fields, and ranked sources. Input should be a question or request in everyday words.";
|
|
23
|
+
name = "specstream_search";
|
|
24
|
+
schema = inputSchema;
|
|
25
|
+
passages;
|
|
26
|
+
apiWrapper;
|
|
27
|
+
constructor(params = {}) {
|
|
28
|
+
super(params);
|
|
29
|
+
if (params.name) {
|
|
30
|
+
this.name = params.name;
|
|
31
|
+
}
|
|
32
|
+
if (params.description) {
|
|
33
|
+
this.description = params.description;
|
|
34
|
+
}
|
|
35
|
+
this.passages = params.passages;
|
|
36
|
+
this.apiWrapper =
|
|
37
|
+
params.apiWrapper ??
|
|
38
|
+
new SpecStreamSearchAPIWrapper({
|
|
39
|
+
specstreamApiKey: params.specstreamApiKey,
|
|
40
|
+
apiBaseUrl: params.apiBaseUrl,
|
|
41
|
+
});
|
|
42
|
+
}
|
|
43
|
+
async _call(input, _runManager) {
|
|
44
|
+
try {
|
|
45
|
+
return await this.apiWrapper.rawResults({ q: input.query, passages: this.passages });
|
|
46
|
+
}
|
|
47
|
+
catch (e) {
|
|
48
|
+
return { error: e instanceof Error ? e.message : String(e) };
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
}
|
package/dist/utils.d.ts
ADDED
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
export declare const SPECSTREAM_BASE_URL = "https://api.specstream.ai";
|
|
2
|
+
export declare const MAX_EXTRACT_URLS = 20;
|
|
3
|
+
export type SpecStreamSearchParams = {
|
|
4
|
+
/** The question or request in everyday words, 1 to 2,000 characters. */
|
|
5
|
+
q: string;
|
|
6
|
+
/** Add the text of the top pages to the reply. */
|
|
7
|
+
passages?: boolean;
|
|
8
|
+
};
|
|
9
|
+
export type SpecStreamSource = {
|
|
10
|
+
url: string;
|
|
11
|
+
title?: string;
|
|
12
|
+
site?: string;
|
|
13
|
+
snippet?: string;
|
|
14
|
+
published?: string;
|
|
15
|
+
cited?: boolean;
|
|
16
|
+
};
|
|
17
|
+
export type SpecStreamSearchResponse = {
|
|
18
|
+
id?: string;
|
|
19
|
+
request_id?: string;
|
|
20
|
+
kind: "fact" | "list";
|
|
21
|
+
query?: string;
|
|
22
|
+
columns?: unknown[];
|
|
23
|
+
answer: {
|
|
24
|
+
value: string;
|
|
25
|
+
page: string;
|
|
26
|
+
seen_at?: string;
|
|
27
|
+
} | null;
|
|
28
|
+
rows: Array<{
|
|
29
|
+
name?: string;
|
|
30
|
+
page?: string;
|
|
31
|
+
snippet?: string;
|
|
32
|
+
probability?: number;
|
|
33
|
+
sources?: string[];
|
|
34
|
+
fields?: Record<string, unknown>;
|
|
35
|
+
}>;
|
|
36
|
+
sources: SpecStreamSource[];
|
|
37
|
+
passages?: Array<{
|
|
38
|
+
url: string;
|
|
39
|
+
title?: string;
|
|
40
|
+
text: string;
|
|
41
|
+
}>;
|
|
42
|
+
handoff?: Record<string, unknown>;
|
|
43
|
+
took_ms?: number;
|
|
44
|
+
price_usd?: number;
|
|
45
|
+
};
|
|
46
|
+
export type SpecStreamExtractParams = {
|
|
47
|
+
/** 1 to 20 http or https URLs. */
|
|
48
|
+
urls: string[];
|
|
49
|
+
format?: "markdown" | "text";
|
|
50
|
+
};
|
|
51
|
+
export type SpecStreamExtractResult = {
|
|
52
|
+
url: string;
|
|
53
|
+
status: "ok" | "blocked" | "not_found" | "empty" | "unsupported" | "invalid_url" | "failed";
|
|
54
|
+
text?: string;
|
|
55
|
+
title?: string;
|
|
56
|
+
published?: string;
|
|
57
|
+
read_from?: "live" | "site" | "archive";
|
|
58
|
+
captured_at?: string;
|
|
59
|
+
};
|
|
60
|
+
export type SpecStreamExtractResponse = {
|
|
61
|
+
results: SpecStreamExtractResult[];
|
|
62
|
+
request_id?: string;
|
|
63
|
+
took_ms?: number;
|
|
64
|
+
price_usd?: number;
|
|
65
|
+
};
|
|
66
|
+
export type SpecStreamAPIWrapperParams = {
|
|
67
|
+
specstreamApiKey?: string;
|
|
68
|
+
apiBaseUrl?: string;
|
|
69
|
+
};
|
|
70
|
+
declare abstract class BaseSpecStreamAPIWrapper {
|
|
71
|
+
protected specstreamApiKey: string;
|
|
72
|
+
protected apiBaseUrl: string;
|
|
73
|
+
constructor(params?: SpecStreamAPIWrapperParams);
|
|
74
|
+
protected call<T>(method: "GET" | "POST", path: string, body?: Record<string, unknown>): Promise<T>;
|
|
75
|
+
}
|
|
76
|
+
/**
|
|
77
|
+
* A wrapper that encapsulates access to the SpecStream Search API
|
|
78
|
+
* (`GET /v1/web-search`). Primarily used for testing.
|
|
79
|
+
*/
|
|
80
|
+
export declare class SpecStreamSearchAPIWrapper extends BaseSpecStreamAPIWrapper {
|
|
81
|
+
rawResults(params: SpecStreamSearchParams): Promise<SpecStreamSearchResponse>;
|
|
82
|
+
}
|
|
83
|
+
/**
|
|
84
|
+
* A wrapper that encapsulates access to the SpecStream Extract API
|
|
85
|
+
* (`POST /v1/extract`). Primarily used for testing.
|
|
86
|
+
*/
|
|
87
|
+
export declare class SpecStreamExtractAPIWrapper extends BaseSpecStreamAPIWrapper {
|
|
88
|
+
rawResults(params: SpecStreamExtractParams): Promise<SpecStreamExtractResponse>;
|
|
89
|
+
}
|
|
90
|
+
export {};
|
package/dist/utils.js
ADDED
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
import { getEnvironmentVariable } from "@langchain/core/utils/env";
|
|
2
|
+
export const SPECSTREAM_BASE_URL = "https://api.specstream.ai";
|
|
3
|
+
export const MAX_EXTRACT_URLS = 20;
|
|
4
|
+
class BaseSpecStreamAPIWrapper {
|
|
5
|
+
specstreamApiKey;
|
|
6
|
+
apiBaseUrl;
|
|
7
|
+
constructor(params = {}) {
|
|
8
|
+
const apiKey = (params.specstreamApiKey ?? getEnvironmentVariable("SPECSTREAM_API_KEY") ?? "").trim();
|
|
9
|
+
if (!apiKey) {
|
|
10
|
+
throw new Error("No SpecStream API key found. Pass specstreamApiKey or set SPECSTREAM_API_KEY. Create a key under Agents & API in your SpecStream workspace.");
|
|
11
|
+
}
|
|
12
|
+
this.specstreamApiKey = apiKey.replace(/^bearer\s+/i, "");
|
|
13
|
+
this.apiBaseUrl = (params.apiBaseUrl ?? getEnvironmentVariable("SPECSTREAM_BASE_URL") ?? SPECSTREAM_BASE_URL).replace(/\/+$/, "");
|
|
14
|
+
}
|
|
15
|
+
async call(method, path, body) {
|
|
16
|
+
const response = await fetch(`${this.apiBaseUrl}${path}`, {
|
|
17
|
+
method,
|
|
18
|
+
headers: {
|
|
19
|
+
Authorization: `Bearer ${this.specstreamApiKey}`,
|
|
20
|
+
Accept: "application/json",
|
|
21
|
+
...(body === undefined ? {} : { "Content-Type": "application/json" }),
|
|
22
|
+
"User-Agent": "@specstream/langchain",
|
|
23
|
+
},
|
|
24
|
+
body: body === undefined ? undefined : JSON.stringify(body),
|
|
25
|
+
});
|
|
26
|
+
let payload = {};
|
|
27
|
+
try {
|
|
28
|
+
payload = await response.json();
|
|
29
|
+
}
|
|
30
|
+
catch {
|
|
31
|
+
// A reply that is not JSON is reported by its status below.
|
|
32
|
+
}
|
|
33
|
+
if (!response.ok || !payload?.data) {
|
|
34
|
+
throw new Error(`Error ${response.status}: ${payload?.error?.message ?? "Unknown error"}`);
|
|
35
|
+
}
|
|
36
|
+
return payload.data;
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* A wrapper that encapsulates access to the SpecStream Search API
|
|
41
|
+
* (`GET /v1/web-search`). Primarily used for testing.
|
|
42
|
+
*/
|
|
43
|
+
export class SpecStreamSearchAPIWrapper extends BaseSpecStreamAPIWrapper {
|
|
44
|
+
async rawResults(params) {
|
|
45
|
+
const query = new URLSearchParams({ q: params.q.trim() });
|
|
46
|
+
if (params.passages) {
|
|
47
|
+
query.set("passages", "true");
|
|
48
|
+
}
|
|
49
|
+
return this.call("GET", `/v1/web-search?${query.toString()}`);
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
/**
|
|
53
|
+
* A wrapper that encapsulates access to the SpecStream Extract API
|
|
54
|
+
* (`POST /v1/extract`). Primarily used for testing.
|
|
55
|
+
*/
|
|
56
|
+
export class SpecStreamExtractAPIWrapper extends BaseSpecStreamAPIWrapper {
|
|
57
|
+
async rawResults(params) {
|
|
58
|
+
const urls = params.urls.map((url) => url.trim()).filter(Boolean).slice(0, MAX_EXTRACT_URLS);
|
|
59
|
+
return this.call("POST", "/v1/extract", {
|
|
60
|
+
urls,
|
|
61
|
+
format: params.format === "text" ? "text" : "markdown",
|
|
62
|
+
});
|
|
63
|
+
}
|
|
64
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,77 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@specstream/langchain",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"
|
|
5
|
-
"
|
|
6
|
-
|
|
3
|
+
"version": "0.1.1",
|
|
4
|
+
"description": "The live web data engine for LangChain.js: Search API ($5 per 1,000 searches), Extract API ($0.80 per 1,000 pages) and Deep Search.",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"engines": {
|
|
7
|
+
"node": ">=20"
|
|
8
|
+
},
|
|
9
|
+
"main": "./dist/cjs/index.js",
|
|
10
|
+
"types": "./dist/index.d.ts",
|
|
11
|
+
"exports": {
|
|
12
|
+
".": {
|
|
13
|
+
"import": {
|
|
14
|
+
"types": "./dist/index.d.ts",
|
|
15
|
+
"default": "./dist/index.js"
|
|
16
|
+
},
|
|
17
|
+
"require": {
|
|
18
|
+
"types": "./dist/cjs/index.d.ts",
|
|
19
|
+
"default": "./dist/cjs/index.js"
|
|
20
|
+
}
|
|
21
|
+
},
|
|
22
|
+
"./package.json": "./package.json"
|
|
23
|
+
},
|
|
24
|
+
"files": [
|
|
25
|
+
"dist",
|
|
26
|
+
"README.md",
|
|
27
|
+
"LICENSE"
|
|
28
|
+
],
|
|
29
|
+
"sideEffects": false,
|
|
30
|
+
"scripts": {
|
|
31
|
+
"build": "rm -rf dist && tsc -p tsconfig.json && tsc -p tsconfig.cjs.json && node -e \"require('fs').writeFileSync('dist/cjs/package.json','{\\\"type\\\":\\\"commonjs\\\"}\\n')\"",
|
|
32
|
+
"typecheck": "tsc -p tsconfig.test.json",
|
|
33
|
+
"test": "vitest run",
|
|
34
|
+
"prepack": "npm run build"
|
|
35
|
+
},
|
|
36
|
+
"dependencies": {
|
|
37
|
+
"zod": "^3.25.76 || ^4"
|
|
38
|
+
},
|
|
39
|
+
"peerDependencies": {
|
|
40
|
+
"@langchain/core": "^1.0.0"
|
|
41
|
+
},
|
|
42
|
+
"devDependencies": {
|
|
43
|
+
"@langchain/core": "^1.0.0",
|
|
44
|
+
"@types/node": "^22.0.0",
|
|
45
|
+
"typescript": "5.9.3",
|
|
46
|
+
"vitest": "^3.2.4"
|
|
47
|
+
},
|
|
48
|
+
"keywords": [
|
|
49
|
+
"langchain",
|
|
50
|
+
"langchainjs",
|
|
51
|
+
"specstream",
|
|
52
|
+
"search-api",
|
|
53
|
+
"extract-api",
|
|
54
|
+
"web-search",
|
|
55
|
+
"web-data",
|
|
56
|
+
"live-web",
|
|
57
|
+
"agents"
|
|
58
|
+
],
|
|
59
|
+
"author": {
|
|
60
|
+
"name": "SpecStream",
|
|
61
|
+
"email": "support@specstream.ai",
|
|
62
|
+
"url": "https://specstream.ai"
|
|
63
|
+
},
|
|
64
|
+
"license": "MIT",
|
|
65
|
+
"homepage": "https://specstream.ai/docs/langchain",
|
|
66
|
+
"repository": {
|
|
67
|
+
"type": "git",
|
|
68
|
+
"url": "git+https://github.com/specstream/specstream-tools.git",
|
|
69
|
+
"directory": "packages/langchain-js"
|
|
70
|
+
},
|
|
71
|
+
"bugs": {
|
|
72
|
+
"url": "https://github.com/specstream/specstream-tools/issues"
|
|
73
|
+
},
|
|
74
|
+
"publishConfig": {
|
|
75
|
+
"access": "public"
|
|
76
|
+
}
|
|
77
|
+
}
|