@takoviz/ai-sdk 3.0.0 → 4.0.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/README.md +51 -66
- package/dist/index.d.ts +90 -328
- package/dist/index.js +79 -125
- package/package.json +10 -9
package/README.md
CHANGED
|
@@ -55,87 +55,68 @@ const tools = {
|
|
|
55
55
|
|
|
56
56
|
## Configuration
|
|
57
57
|
|
|
58
|
-
`
|
|
58
|
+
A tool's config is the API request body for its endpoint, minus the field the model supplies, plus `apiKey` and `baseUrl`. Keys are the API's own names, exactly as [`tako-sdk`](https://www.npmjs.com/package/tako-sdk) declares them, so every option in the [API reference](https://docs.tako.com) works here without a release of this package.
|
|
59
|
+
|
|
60
|
+
| Tool | Config type | Is the request body of | Minus |
|
|
61
|
+
| --- | --- | --- | --- |
|
|
62
|
+
| `takoSearch` | `TakoRetrievalConfig` | `POST /api/v3/search` | `query` |
|
|
63
|
+
| `takoAnswer` | `TakoAnswerConfig` | `POST /api/v1/answer` | `query` |
|
|
64
|
+
| `takoContents` | `TakoContentsConfig` | `POST /api/v1/contents` | `url` |
|
|
65
|
+
|
|
66
|
+
Every field is optional. Omit one and the API's default applies; this package restates none of them. The model supplies only `{ query }` or `{ url }` per call.
|
|
67
|
+
|
|
68
|
+
Three `sources.data` keys aren't exposed, because you can't use them correctly from here: `mode` (the API documents it as having no effect on Tako cards), and `node_ids` with `strict` (they take graph ids from endpoints this package doesn't wrap). Call the API through `tako-sdk` directly if you need them.
|
|
69
|
+
|
|
70
|
+
### Examples
|
|
71
|
+
|
|
72
|
+
Deep search over Tako's data only, ten cards, with the rows inlined as typed columns:
|
|
59
73
|
|
|
60
74
|
```typescript
|
|
61
75
|
takoSearch({
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
effort: 'fast', // 'fast' (default) | 'instant' | 'deep'
|
|
65
|
-
sources: { // a source is searched iff its key is present; omit to search both
|
|
66
|
-
data: { count: 5, includeContents: false }, // legacy alias: tako
|
|
67
|
-
web: { count: 5, includeContents: false },
|
|
68
|
-
},
|
|
69
|
-
countryCode: 'US', // default 'US'
|
|
70
|
-
locale: 'en-US', // default 'en-US'
|
|
71
|
-
timezone: 'America/New_York',// optional IANA timezone
|
|
72
|
-
outputSettings: {
|
|
73
|
-
imageDarkMode: false,
|
|
74
|
-
forceRefresh: false, // instant mode only
|
|
75
|
-
},
|
|
76
|
+
effort: 'deep',
|
|
77
|
+
sources: { data: { count: 10, include_contents: true, content_format: 'json_compact' } },
|
|
76
78
|
});
|
|
77
79
|
```
|
|
78
80
|
|
|
79
|
-
`
|
|
81
|
+
News from the last week. Build dates with the ISO-string constructor: `new Date('2026-08-19')` is UTC midnight and serializes as that day everywhere; `new Date(2026, 7, 19)` is local midnight and serializes as the day before in any UTC+ timezone.
|
|
82
|
+
|
|
83
|
+
Check that your date parsed before you pass it. `new Date()` returns an `Invalid Date` for a string it can't read, and that fails during serialization, so the tool call rejects with `Failed to search with Tako: Invalid time value` — a message that names neither the field nor the value, and reaches the model rather than you. The request never leaves the process.
|
|
80
84
|
|
|
81
85
|
```typescript
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
baseUrl: 'https://tako.com',
|
|
85
|
-
mode: 'url', // 'url' (default) → presigned link; 'inline' → content in the response
|
|
86
|
+
takoSearch({
|
|
87
|
+
sources: { web: { category: 'news', published_after: new Date('2026-08-19'), count: 5 } },
|
|
86
88
|
});
|
|
87
89
|
```
|
|
88
90
|
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
### Search and answer options
|
|
92
|
-
|
|
93
|
-
Both tools take the same config. Every field is optional; omit one and the API's default applies.
|
|
91
|
+
An answer shaped by a JSON Schema. Tako fills it from the same evidence as `answer` and returns it as `structured_output`; a nullable type is how you let it say "no evidence" instead of inventing a zero.
|
|
94
92
|
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
| `includeContents` | `boolean` | Inline the card data. |
|
|
109
|
-
| `contentFormat` | `"csv" \| "json_records" \| "json_compact"` | Server default `"json_compact"`. |
|
|
110
|
-
| `nodeIds` | `string[]` | Pin graph nodes. Ids come from the `/v1/graph` endpoints. |
|
|
111
|
-
| `strict` | `boolean` | Return only cards matching a pinned node. Requires `nodeIds`. |
|
|
112
|
-
| `mode` | `"url" \| "inline"` | Server default `"inline"`. Accepted, but the API documents no effect on Tako cards. |
|
|
93
|
+
```typescript
|
|
94
|
+
takoAnswer({
|
|
95
|
+
output_schema: {
|
|
96
|
+
type: 'object',
|
|
97
|
+
properties: {
|
|
98
|
+
revenue_usd: { type: ['number', 'null'], description: 'Latest annual revenue in USD' },
|
|
99
|
+
fiscal_year: { type: ['integer', 'null'] },
|
|
100
|
+
},
|
|
101
|
+
required: ['revenue_usd', 'fiscal_year'],
|
|
102
|
+
additionalProperties: false,
|
|
103
|
+
},
|
|
104
|
+
});
|
|
105
|
+
```
|
|
113
106
|
|
|
114
|
-
|
|
107
|
+
A `TakoRetrievalConfig` is also a valid `TakoAnswerConfig`, so one object can build both tools.
|
|
115
108
|
|
|
116
|
-
|
|
117
|
-
| --- | --- | --- |
|
|
118
|
-
| `count` | `number` | 1-20. Server default 5 for `takoSearch`, 3 for `takoAnswer`. |
|
|
119
|
-
| `includeContents` | `boolean` | Include full article text. |
|
|
120
|
-
| `category` | `"news" \| "sports" \| "finance"` | Only `"news"` filters today. |
|
|
121
|
-
| `includeDomains` / `excludeDomains` | `string[]` | Bare hosts, for example `"cnn.com"`. |
|
|
122
|
-
| `publishedAfter` / `publishedBefore` | `string` | ISO `"YYYY-MM-DD"`. Results with no known date are kept. |
|
|
123
|
-
| `snippetMaxChars` | `number` | Server default 1000. |
|
|
124
|
-
| `articleContentMaxChars` | `number` | Server default 30000. |
|
|
109
|
+
### Contents delivery
|
|
125
110
|
|
|
126
|
-
|
|
111
|
+
Two `takoContents` options also change the description the model reads, so pick them deliberately:
|
|
127
112
|
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
| `contentFormat` | `"csv" \| "json_records" \| "json_compact"` | Server default `"csv"` on this surface. |
|
|
132
|
-
| `maxRows` | `number` | Card exports only. The first 20 rows are free; **rows above that bill at the per-1000-row rate**. |
|
|
133
|
-
| `maxChars` | `number` | Web page text only. Server default 1000000, the full page text. |
|
|
134
|
-
| `quoteOnly` | `boolean` | Price the export without fetching it. The request is free and the payload is null. |
|
|
113
|
+
- `mode: 'url'` returns a short-lived presigned download link. The description tells the model to surface the link, not parse it.
|
|
114
|
+
- `mode: 'inline'` returns the rows or page text in the response. The description tells the model to read and compute over them.
|
|
115
|
+
- `quote_only: true` returns the export price and no content, for free. The description tells the model to report the price and not call again expecting rows.
|
|
135
116
|
|
|
136
|
-
|
|
117
|
+
Leave `mode` unset and the API chooses — `'url'` today. This package sends no default and names none in the description; the model is told to read the response instead. Set `mode` to pin the delivery and tell the model which one to expect.
|
|
137
118
|
|
|
138
|
-
|
|
119
|
+
`max_rows` fails quietly: a value over the 2,000-row ceiling is clamped, not rejected, and every row returned is billed. Read `total_rows` and `truncated` on the item to see what you got.
|
|
139
120
|
|
|
140
121
|
## Responses
|
|
141
122
|
|
|
@@ -176,7 +157,7 @@ Each item carries a `cost` (USD) and either a presigned `url` + `expires_at` (ur
|
|
|
176
157
|
|
|
177
158
|
`total_rows` and `truncated` tell you whether the card held more rows than were returned.
|
|
178
159
|
|
|
179
|
-
Which format you get depends on the surface. Left unset, `takoContents` returns `'csv'` for cards and no format for web pages, while a card inlined by `sources.data.
|
|
160
|
+
Which format you get depends on the surface. Left unset, `takoContents` returns `'csv'` for cards and no format for web pages, while a card inlined by `sources.data.include_contents` arrives as `'json_compact'` (a `dataset`). Set `content_format` to choose: on `takoContents` for an explicit fetch, or on `sources.data` for a card inlined by a search.
|
|
180
161
|
|
|
181
162
|
`content_format` is optional as well as nullable, so branch on it loosely — `content_format == null` means web text; `=== null` misses the absent case.
|
|
182
163
|
|
|
@@ -187,6 +168,7 @@ Full type definitions ship with the package.
|
|
|
187
168
|
```typescript
|
|
188
169
|
import type {
|
|
189
170
|
TakoRetrievalConfig,
|
|
171
|
+
TakoAnswerConfig,
|
|
190
172
|
TakoContentsConfig,
|
|
191
173
|
TakoSearchResult,
|
|
192
174
|
TakoAnswerResult,
|
|
@@ -200,7 +182,9 @@ import type {
|
|
|
200
182
|
} from '@takoviz/ai-sdk';
|
|
201
183
|
```
|
|
202
184
|
|
|
203
|
-
|
|
185
|
+
Every wire type is [`tako-sdk`](https://www.npmjs.com/package/tako-sdk)'s, Tako's client generated from the OpenAPI spec, re-exported under the names above. This package declares only its config types and the normalized tool results. When the API adds a field, it appears here as soon as `tako-sdk` publishes — no release of this package needed.
|
|
186
|
+
|
|
187
|
+
`tako-sdk` is a runtime dependency. It uses the global `fetch` and runs wherever this package does.
|
|
204
188
|
|
|
205
189
|
If you need the raw wire shapes (where collections are optional, before the tools normalize them), import `TakoSearchResponse`, `TakoAnswerResponse` or `TakoContentsResponse`.
|
|
206
190
|
|
|
@@ -210,7 +194,8 @@ MIT
|
|
|
210
194
|
|
|
211
195
|
## Links
|
|
212
196
|
|
|
213
|
-
- [Migrating from
|
|
197
|
+
- [Migrating from 3.x](./MIGRATING.md#3x--40)
|
|
198
|
+
- [Migrating from 2.x](./MIGRATING.md#2x--30)
|
|
214
199
|
- [Tako documentation](https://docs.tako.com)
|
|
215
200
|
- [Vercel AI SDK](https://sdk.vercel.ai/docs)
|
|
216
201
|
- [GitHub repository](https://github.com/TakoData/ai-sdk)
|
package/dist/index.d.ts
CHANGED
|
@@ -1,363 +1,122 @@
|
|
|
1
1
|
import { Tool } from 'ai';
|
|
2
|
+
import * as sdk from 'tako-sdk';
|
|
2
3
|
|
|
3
|
-
type TakoSearchEffort =
|
|
4
|
-
type TakoContentsMode =
|
|
4
|
+
type TakoSearchEffort = sdk.SearchEffortLevel;
|
|
5
|
+
type TakoContentsMode = sdk.ContentsDeliveryMode;
|
|
5
6
|
/** Serialization of tabular (Tako card) data. Web text carries no format. */
|
|
6
|
-
type TakoContentFormat =
|
|
7
|
+
type TakoContentFormat = sdk.ContentsFormat;
|
|
7
8
|
/** Public source taxonomy for the card surfaces. */
|
|
8
|
-
type TakoSourceIndex =
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
* `"data" | "web"`. Comparisons against the removed values no longer compile.
|
|
13
|
-
*/
|
|
14
|
-
type TakoCardSourceIndex = TakoSourceIndex;
|
|
15
|
-
type TakoKnowledgeCardRelevance = "High" | "Medium" | "Low";
|
|
16
|
-
type TakoGraphNodeType = "metric" | "entity";
|
|
17
|
-
type TakoDatasetColumnType = "string" | "number" | "boolean" | "date" | "datetime";
|
|
9
|
+
type TakoSourceIndex = sdk.TakoSourceIndex;
|
|
10
|
+
type TakoKnowledgeCardRelevance = sdk.KnowledgeCardRelevance;
|
|
11
|
+
type TakoGraphNodeType = sdk.GraphNodeType;
|
|
12
|
+
type TakoDatasetColumnType = sdk.TakoDatasetColumnType;
|
|
18
13
|
/** Web result category. Only "news" filters today; the others are accepted and inert. */
|
|
19
|
-
type TakoWebCategory =
|
|
14
|
+
type TakoWebCategory = sdk.WebCategory;
|
|
20
15
|
interface TakoBaseConfig {
|
|
21
16
|
/** Tako API key. Falls back to TAKO_API_KEY / TAKO_API_TOKEN env vars. */
|
|
22
17
|
apiKey?: string;
|
|
23
18
|
/** API base URL. Default "https://tako.com". */
|
|
24
19
|
baseUrl?: string;
|
|
25
20
|
}
|
|
26
|
-
interface TakoSourceOptions {
|
|
27
|
-
/**
|
|
28
|
-
* Max results for this source, 1-20.
|
|
29
|
-
*
|
|
30
|
-
* The server default differs by tool: `takoSearch` returns 5, `takoAnswer`
|
|
31
|
-
* returns 3. Set this value when you need the same count from both.
|
|
32
|
-
*/
|
|
33
|
-
count?: number;
|
|
34
|
-
/** Inline this source's underlying data in the response. */
|
|
35
|
-
includeContents?: boolean;
|
|
36
|
-
}
|
|
37
|
-
/** Options for the curated Tako data source. Mirrors the API's `DataSourceSettings`. */
|
|
38
|
-
interface TakoDataSourceOptions extends TakoSourceOptions {
|
|
39
|
-
/**
|
|
40
|
-
* Delivery for card data inlined by this search.
|
|
41
|
-
*
|
|
42
|
-
* The API documents this field as having no effect on Tako cards, which always
|
|
43
|
-
* return a small inline preview. It stays for schema stability. This is a
|
|
44
|
-
* different field from {@link TakoContentsConfig.mode}, which does control
|
|
45
|
-
* delivery for an explicit contents call.
|
|
46
|
-
*/
|
|
47
|
-
mode?: TakoContentsMode;
|
|
48
|
-
/** Serialization for inlined card data. Server default "json_compact". */
|
|
49
|
-
contentFormat?: TakoContentFormat;
|
|
50
|
-
/**
|
|
51
|
-
* Graph node ids to pin into the search. Get ids from the /v1/graph endpoints,
|
|
52
|
-
* which this SDK does not wrap. Ids do not survive a knowledge-graph rebuild:
|
|
53
|
-
* resolve them per request rather than storing them.
|
|
54
|
-
*/
|
|
55
|
-
nodeIds?: string[];
|
|
56
|
-
/** Return only cards that match a pinned node. Requires a non-empty `nodeIds`. */
|
|
57
|
-
strict?: boolean;
|
|
58
|
-
}
|
|
59
|
-
/** Options for the web source. Mirrors the API's `WebSourceSettings`. */
|
|
60
|
-
interface TakoWebSourceOptions extends TakoSourceOptions {
|
|
61
|
-
/** Restrict web results to a category. */
|
|
62
|
-
category?: TakoWebCategory;
|
|
63
|
-
/** Return only results from these bare hosts, for example "cnn.com". */
|
|
64
|
-
includeDomains?: string[];
|
|
65
|
-
/** Drop results from these bare hosts. */
|
|
66
|
-
excludeDomains?: string[];
|
|
67
|
-
/** Character cap on the excerpt per web result. Server default 1000. */
|
|
68
|
-
snippetMaxChars?: number;
|
|
69
|
-
/** Character cap on full article text when `includeContents` is true. Server default 30000. */
|
|
70
|
-
articleContentMaxChars?: number;
|
|
71
|
-
/**
|
|
72
|
-
* Keep results published on or after this ISO date, "YYYY-MM-DD".
|
|
73
|
-
*
|
|
74
|
-
* This is not a recency guarantee. The API keeps a result whose publication
|
|
75
|
-
* date it does not know, so undated pages still arrive.
|
|
76
|
-
*/
|
|
77
|
-
publishedAfter?: string;
|
|
78
|
-
/**
|
|
79
|
-
* Keep results published on or before this ISO date, "YYYY-MM-DD".
|
|
80
|
-
*
|
|
81
|
-
* The API keeps a result whose publication date it does not know.
|
|
82
|
-
*/
|
|
83
|
-
publishedBefore?: string;
|
|
84
|
-
}
|
|
85
|
-
/** @deprecated Renamed to {@link TakoDataSourceOptions}. */
|
|
86
|
-
type TakoCardSourceOptions = TakoDataSourceOptions;
|
|
87
|
-
/** End-user coordinates used to localize results. */
|
|
88
|
-
interface TakoGeoLocation {
|
|
89
|
-
/** Degrees, -90 to 90. */
|
|
90
|
-
latitude: number;
|
|
91
|
-
/** Degrees, -180 to 180. */
|
|
92
|
-
longitude: number;
|
|
93
|
-
}
|
|
94
|
-
interface TakoRetrievalConfig extends TakoBaseConfig {
|
|
95
|
-
/** "fast" (default) | "instant" | "deep". */
|
|
96
|
-
effort?: TakoSearchEffort;
|
|
97
|
-
/** Per-source settings. A source is searched iff its key is present. Omit to search data + web. */
|
|
98
|
-
sources?: {
|
|
99
|
-
/** The curated Tako data source. */
|
|
100
|
-
data?: TakoDataSourceOptions;
|
|
101
|
-
web?: TakoWebSourceOptions;
|
|
102
|
-
/** @deprecated Use `data`. Legacy alias for the curated Tako source. */
|
|
103
|
-
tako?: TakoDataSourceOptions;
|
|
104
|
-
};
|
|
105
|
-
/** End-user coordinates. Use with `countryCode` for location-sensitive queries. */
|
|
106
|
-
location?: TakoGeoLocation;
|
|
107
|
-
/** ISO 3166-1 alpha-2 country code. Default "US". */
|
|
108
|
-
countryCode?: string;
|
|
109
|
-
/** BCP-47 locale tag. Default "en-US". */
|
|
110
|
-
locale?: string;
|
|
111
|
-
/** IANA timezone, e.g. "America/New_York". */
|
|
112
|
-
timezone?: string;
|
|
113
|
-
outputSettings?: {
|
|
114
|
-
imageDarkMode?: boolean;
|
|
115
|
-
/** Instant mode only. */
|
|
116
|
-
forceRefresh?: boolean;
|
|
117
|
-
};
|
|
118
|
-
}
|
|
119
|
-
interface TakoContentsConfig extends TakoBaseConfig {
|
|
120
|
-
/** "url" (default) returns a presigned link; "inline" returns content in the body. */
|
|
121
|
-
mode?: TakoContentsMode;
|
|
122
|
-
/** Serialization for card data. Server default "csv" on this surface. */
|
|
123
|
-
contentFormat?: TakoContentFormat;
|
|
124
|
-
/**
|
|
125
|
-
* Cap on rows returned for a card export. The server default is the 20-row free
|
|
126
|
-
* allowance. Rows above that allowance bill at the per-1000-row rate, so raise
|
|
127
|
-
* this only when you need the extra rows. Web urls ignore this field.
|
|
128
|
-
*/
|
|
129
|
-
maxRows?: number;
|
|
130
|
-
/** Character cap on extracted web page text. Server default 1000000, the full page text. Card urls ignore this field. */
|
|
131
|
-
maxChars?: number;
|
|
132
|
-
/**
|
|
133
|
-
* Return only the price of the export, without the content. The request is free
|
|
134
|
-
* and the item's payload and url are null. The server ignores `mode` and
|
|
135
|
-
* `contentFormat`.
|
|
136
|
-
*/
|
|
137
|
-
quoteOnly?: boolean;
|
|
138
|
-
}
|
|
139
|
-
interface TakoUsageCompute {
|
|
140
|
-
/** USD cost of running the operation. */
|
|
141
|
-
cost_usd: number;
|
|
142
|
-
}
|
|
143
|
-
interface TakoUsageData {
|
|
144
|
-
/** USD cost of the inline data delivered in the response. */
|
|
145
|
-
cost_usd: number;
|
|
146
|
-
/** Number of billed data units (datasets) in the response. */
|
|
147
|
-
datasets: number;
|
|
148
|
-
}
|
|
149
21
|
/**
|
|
150
|
-
*
|
|
151
|
-
*
|
|
22
|
+
* The curated Tako data source. Three `DataSourceSettings` keys are omitted
|
|
23
|
+
* because you can't use them correctly from this package:
|
|
152
24
|
*
|
|
153
|
-
*
|
|
154
|
-
*
|
|
155
|
-
*
|
|
156
|
-
*
|
|
157
|
-
* and
|
|
25
|
+
* - `mode` — the API documents it as having no effect on Tako cards, which
|
|
26
|
+
* always inline rows. Setting `"url"` looks like it should return download
|
|
27
|
+
* links and doesn't.
|
|
28
|
+
* - `node_ids` — takes ids from the `/v1/graph` endpoints, which this package
|
|
29
|
+
* doesn't wrap, and the ids don't survive a knowledge-graph rebuild.
|
|
30
|
+
* - `strict` — only meaningful with `node_ids`.
|
|
31
|
+
*
|
|
32
|
+
* The omission is a type. The API accepts the keys if you send them anyway.
|
|
158
33
|
*/
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
/** Compute breakdown. Absent on surfaces with no compute step (contents). */
|
|
163
|
-
compute?: TakoUsageCompute | null;
|
|
164
|
-
/** Inline-data breakdown. Present only when billable inline data was emitted. */
|
|
165
|
-
data?: TakoUsageData | null;
|
|
166
|
-
}
|
|
167
|
-
interface TakoDatasetColumn {
|
|
168
|
-
name: string;
|
|
169
|
-
type: TakoDatasetColumnType;
|
|
170
|
-
/** Structured unit, e.g. "USD billions", "%". Null when unitless. */
|
|
171
|
-
unit?: string | null;
|
|
172
|
-
}
|
|
173
|
-
interface TakoDatasetSource {
|
|
174
|
-
/** Human-readable source name, e.g. "FRED". */
|
|
175
|
-
name: string;
|
|
176
|
-
index?: TakoSourceIndex;
|
|
177
|
-
}
|
|
178
|
-
type TakoDatasetCell = string | number | boolean | null;
|
|
179
|
-
/** Exact retrieved rows as positional arrays in `columns` order. */
|
|
180
|
-
interface TakoDataset {
|
|
181
|
-
columns: TakoDatasetColumn[];
|
|
182
|
-
rows: TakoDatasetCell[][];
|
|
183
|
-
total_rows: number;
|
|
184
|
-
truncated: boolean;
|
|
185
|
-
/** Source URL the dataset was derived from. */
|
|
186
|
-
ref: string;
|
|
187
|
-
sources: TakoDatasetSource[];
|
|
188
|
-
provenance?: "query" | "web_extraction";
|
|
189
|
-
}
|
|
34
|
+
type TakoDataSourceOptions = Omit<sdk.DataSourceSettings, "mode" | "node_ids" | "strict">;
|
|
35
|
+
/** The web source. Every `WebSourceSettings` key, unchanged. */
|
|
36
|
+
type TakoWebSourceOptions = sdk.WebSourceSettings;
|
|
190
37
|
/**
|
|
191
|
-
*
|
|
192
|
-
*
|
|
38
|
+
* Tako searches exactly the sources whose keys are present. Omit `sources` to
|
|
39
|
+
* search data and web.
|
|
40
|
+
*
|
|
41
|
+
* Derived from `sdk.Sources`, not a literal `{ data, web }` pair. Spelling the
|
|
42
|
+
* pair out is the one edit that silently breaks this file's promise: a source
|
|
43
|
+
* Tako adds would serialize on the wire and still be unreachable here, and the
|
|
44
|
+
* `keyof` pin in `config_types.test.ts` would stay green because a hand-written
|
|
45
|
+
* source set is what forced it to exclude `sources` in the first place.
|
|
193
46
|
*/
|
|
194
|
-
interface
|
|
195
|
-
|
|
196
|
-
row_cpm_usd: number;
|
|
197
|
-
free_rows: number;
|
|
198
|
-
max_rows_ceiling: number;
|
|
47
|
+
interface TakoSources extends Omit<sdk.Sources, "data"> {
|
|
48
|
+
data?: TakoDataSourceOptions;
|
|
199
49
|
}
|
|
200
|
-
/**
|
|
201
|
-
interface
|
|
202
|
-
|
|
203
|
-
metric?: string | null;
|
|
204
|
-
entity?: string | null;
|
|
205
|
-
unit?: string | null;
|
|
206
|
-
dtype?: TakoDatasetColumnType | null;
|
|
50
|
+
/** Config for `takoSearch`: the `/v3/search` request body without `query`. */
|
|
51
|
+
interface TakoRetrievalConfig extends TakoBaseConfig, Omit<sdk.SearchRequest, "query" | "sources"> {
|
|
52
|
+
sources?: TakoSources;
|
|
207
53
|
}
|
|
208
54
|
/**
|
|
209
|
-
*
|
|
210
|
-
*
|
|
211
|
-
*
|
|
212
|
-
*
|
|
213
|
-
* `url` + `expires_at` (presigned download). When every payload field is unset
|
|
214
|
-
* this is just a price quote.
|
|
55
|
+
* Config for `takoAnswer`: the `/v1/answer` request body without `query`.
|
|
56
|
+
* A superset of {@link TakoRetrievalConfig}, so one config object can build
|
|
57
|
+
* both tools. Adds `output_schema`, a JSON Schema Tako fills from the same
|
|
58
|
+
* evidence as `answer` and returns as `structured_output`.
|
|
215
59
|
*
|
|
216
|
-
* `
|
|
217
|
-
*
|
|
218
|
-
* either `null` or an absent key. Test it loosely (`content_format == null`),
|
|
219
|
-
* never with `=== null`.
|
|
60
|
+
* `output_schema` needs `effort` `"fast"` or `"deep"`. `takoAnswer` throws on
|
|
61
|
+
* the pair rather than letting every call 400.
|
|
220
62
|
*/
|
|
221
|
-
interface
|
|
222
|
-
|
|
223
|
-
/** USD price of this item. On search/answer cards this is a prospective /contents quote. */
|
|
224
|
-
cost?: number;
|
|
225
|
-
/** Inline payload as text: CSV card data, or a web page's extracted text. */
|
|
226
|
-
data?: string | null;
|
|
227
|
-
/** Inline card data as row objects keyed by column name ("json_records"). */
|
|
228
|
-
records?: Record<string, TakoDatasetCell>[] | null;
|
|
229
|
-
/** Inline card data as a compact dataset ("json_compact"). */
|
|
230
|
-
dataset?: TakoDataset | null;
|
|
231
|
-
/** Presigned download URL ("url" delivery mode). */
|
|
232
|
-
url?: string | null;
|
|
233
|
-
expires_at?: string | null;
|
|
234
|
-
/** True total rows in the card's data, independent of how many were returned. */
|
|
235
|
-
total_rows?: number | null;
|
|
236
|
-
truncated?: boolean;
|
|
237
|
-
export_pricing?: TakoExportPricing | null;
|
|
238
|
-
manifest?: TakoColumnDescriptor[] | null;
|
|
239
|
-
}
|
|
240
|
-
interface TakoContentItem extends TakoResultContent {
|
|
241
|
-
/** The originating result URL from the request. */
|
|
242
|
-
source_url: string;
|
|
243
|
-
}
|
|
244
|
-
interface TakoCardSource {
|
|
245
|
-
source_name?: string | null;
|
|
246
|
-
source_description?: string | null;
|
|
247
|
-
source_index: TakoSourceIndex;
|
|
248
|
-
url?: string | null;
|
|
249
|
-
/** Raw excerpts from the source page. Present for web sources; null for data. */
|
|
250
|
-
source_text?: string | null;
|
|
251
|
-
}
|
|
252
|
-
/** @deprecated Renamed to {@link TakoCardSource}. */
|
|
253
|
-
type TakoKnowledgeCardSource = TakoCardSource;
|
|
254
|
-
/** Both keys are always present on the wire, though either value may be null. */
|
|
255
|
-
interface TakoKnowledgeCardMethodology {
|
|
256
|
-
methodology_name: string | null;
|
|
257
|
-
methodology_description: string | null;
|
|
258
|
-
}
|
|
259
|
-
/** Graph node (entity or metric) behind a card. */
|
|
260
|
-
interface TakoCardNode {
|
|
261
|
-
/** Opaque public id (`ent::…` / `mt::…`). Not durable across graph rebuilds. */
|
|
262
|
-
id: string;
|
|
263
|
-
type: TakoGraphNodeType;
|
|
264
|
-
name: string;
|
|
265
|
-
description?: string | null;
|
|
266
|
-
}
|
|
267
|
-
interface TakoMetricDefinition {
|
|
268
|
-
name: string;
|
|
269
|
-
definition: string;
|
|
270
|
-
}
|
|
271
|
-
/** Freshness dates for a card's data. */
|
|
272
|
-
interface TakoDataFreshness {
|
|
273
|
-
/** Coverage date of the data. */
|
|
274
|
-
data_as_of?: string | null;
|
|
275
|
-
/** Date the data was last refreshed. */
|
|
276
|
-
last_updated?: string | null;
|
|
277
|
-
}
|
|
278
|
-
interface TakoCard {
|
|
279
|
-
card_id?: string | null;
|
|
280
|
-
title?: string | null;
|
|
281
|
-
description?: string | null;
|
|
282
|
-
semantic_description?: string | null;
|
|
283
|
-
webpage_url?: string | null;
|
|
284
|
-
image_url?: string | null;
|
|
285
|
-
embed_url?: string | null;
|
|
286
|
-
sources?: TakoCardSource[] | null;
|
|
287
|
-
methodologies?: TakoKnowledgeCardMethodology[] | null;
|
|
288
|
-
source_indexes?: TakoSourceIndex[] | null;
|
|
289
|
-
card_type?: string | null;
|
|
290
|
-
relevance?: TakoKnowledgeCardRelevance | null;
|
|
291
|
-
content?: TakoResultContent | null;
|
|
292
|
-
/**
|
|
293
|
-
* Whether /contents can download this card's data. `false` means the export is
|
|
294
|
-
* unavailable — don't call takoContents on it. `true` is eligible but not
|
|
295
|
-
* guaranteed (a 403 is still possible), so fall back to the inline preview.
|
|
296
|
-
*/
|
|
297
|
-
exportable?: boolean;
|
|
298
|
-
/** Relevance on a 1.0–5.0 scale. Only populated for entitled accounts. */
|
|
299
|
-
relevance_score?: number | null;
|
|
300
|
-
/** Graph nodes behind this card. Absent for web-only cards. */
|
|
301
|
-
nodes?: TakoCardNode[] | null;
|
|
302
|
-
metric_definitions?: TakoMetricDefinition[] | null;
|
|
303
|
-
data_freshness?: TakoDataFreshness | null;
|
|
304
|
-
}
|
|
305
|
-
interface TakoWebResult {
|
|
306
|
-
title: string;
|
|
307
|
-
url: string;
|
|
308
|
-
/** Excerpt(s) from the page that matched the query. */
|
|
309
|
-
snippet?: string | null;
|
|
310
|
-
source_name?: string | null;
|
|
311
|
-
publish_date?: string | null;
|
|
312
|
-
content?: TakoResultContent | null;
|
|
313
|
-
/** 1-based citation number for inline [N] markers. Null on raw retrieval. */
|
|
314
|
-
citation_number?: number | null;
|
|
63
|
+
interface TakoAnswerConfig extends TakoBaseConfig, Omit<sdk.AnswerRequest, "query" | "sources"> {
|
|
64
|
+
sources?: TakoSources;
|
|
315
65
|
}
|
|
316
66
|
/**
|
|
317
|
-
*
|
|
318
|
-
*
|
|
319
|
-
* empty. Tools normalize either shape and return {@link TakoSearchResult}.
|
|
67
|
+
* Config for `takoContents`: the `/v1/contents` request body without `url`.
|
|
68
|
+
* `mode` and `quote_only` also change the tool description the model reads.
|
|
320
69
|
*/
|
|
321
|
-
interface
|
|
322
|
-
cards?: TakoCard[];
|
|
323
|
-
web_results?: TakoWebResult[];
|
|
324
|
-
request_id: string;
|
|
325
|
-
usage?: TakoUsage | null;
|
|
70
|
+
interface TakoContentsConfig extends TakoBaseConfig, Omit<sdk.ContentsRequest, "url"> {
|
|
326
71
|
}
|
|
72
|
+
type TakoUsageCompute = sdk.UsageCompute;
|
|
73
|
+
type TakoUsageData = sdk.UsageData;
|
|
74
|
+
/**
|
|
75
|
+
* Usage for one metered request. As of 2026-08 the API doesn't populate it on
|
|
76
|
+
* search, answer or contents. For per-item pricing today, read
|
|
77
|
+
* `TakoResultContent.cost` and `TakoResultContent.export_pricing`.
|
|
78
|
+
*/
|
|
79
|
+
type TakoUsage = sdk.Usage;
|
|
80
|
+
type TakoDatasetColumn = sdk.TakoDatasetColumn;
|
|
81
|
+
type TakoDatasetSource = sdk.TakoDatasetSource;
|
|
82
|
+
type TakoDatasetCell = sdk.TakoDatasetCell;
|
|
83
|
+
type TakoDataset = sdk.TakoDataset;
|
|
84
|
+
type TakoExportPricing = sdk.ExportPricing;
|
|
85
|
+
type TakoColumnDescriptor = sdk.ColumnDescriptor;
|
|
86
|
+
/**
|
|
87
|
+
* Describes the downloadable content behind a result. `content_format` is
|
|
88
|
+
* optional as well as nullable — web text may arrive as `null` or an absent key.
|
|
89
|
+
* Test it loosely (`content_format == null`), never with `=== null`.
|
|
90
|
+
*/
|
|
91
|
+
type TakoResultContent = sdk.ResultContent;
|
|
92
|
+
type TakoContentItem = sdk.ContentItem;
|
|
93
|
+
type TakoCardSource = sdk.TakoCardSource;
|
|
94
|
+
type TakoKnowledgeCardMethodology = sdk.KnowledgeCardMethodology;
|
|
95
|
+
type TakoCardNode = sdk.TakoCardNode;
|
|
96
|
+
type TakoMetricDefinition = sdk.MetricDefinition;
|
|
97
|
+
type TakoDataFreshness = sdk.DataFreshness;
|
|
98
|
+
type TakoCard = sdk.TakoCard;
|
|
99
|
+
type TakoWebResult = sdk.WebResult;
|
|
100
|
+
/** The raw `POST /api/v3/search` body. Only `request_id` is guaranteed. */
|
|
101
|
+
type TakoSearchResponse = sdk.SearchResponse;
|
|
327
102
|
/** The raw `POST /api/v1/answer` body. */
|
|
328
|
-
|
|
329
|
-
answer: string;
|
|
330
|
-
cards?: TakoCard[];
|
|
331
|
-
web_results?: TakoWebResult[];
|
|
332
|
-
request_id: string;
|
|
333
|
-
usage?: TakoUsage | null;
|
|
334
|
-
}
|
|
103
|
+
type TakoAnswerResponse = sdk.AnswerResponse;
|
|
335
104
|
/** The raw `POST /api/v1/contents` body. */
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
request_id: string;
|
|
339
|
-
usage?: TakoUsage | null;
|
|
340
|
-
}
|
|
341
|
-
interface TakoSearchResult {
|
|
105
|
+
type TakoContentsResponse = sdk.ContentsResponse;
|
|
106
|
+
type TakoSearchResult = Omit<TakoSearchResponse, "cards" | "web_results"> & {
|
|
342
107
|
cards: TakoCard[];
|
|
343
108
|
web_results: TakoWebResult[];
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
}
|
|
347
|
-
interface TakoAnswerResult {
|
|
109
|
+
};
|
|
110
|
+
type TakoAnswerResult = Omit<TakoAnswerResponse, "cards" | "web_results"> & {
|
|
348
111
|
/** Synthesized text answer. */
|
|
349
112
|
answer: string;
|
|
350
113
|
/** Backing cards; cards[0] is the lead card. */
|
|
351
114
|
cards: TakoCard[];
|
|
352
115
|
web_results: TakoWebResult[];
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
}
|
|
356
|
-
interface TakoContentsResult {
|
|
116
|
+
};
|
|
117
|
+
type TakoContentsResult = Omit<TakoContentsResponse, "contents"> & {
|
|
357
118
|
contents: TakoContentItem[];
|
|
358
|
-
|
|
359
|
-
usage?: TakoUsage | null;
|
|
360
|
-
}
|
|
119
|
+
};
|
|
361
120
|
|
|
362
121
|
/** Tako fast-pipeline search: returns Tako cards + web results, no LLM synthesis. */
|
|
363
122
|
declare function takoSearch(config?: TakoRetrievalConfig): Tool<{
|
|
@@ -370,7 +129,7 @@ declare function takoSearch(config?: TakoRetrievalConfig): Tool<{
|
|
|
370
129
|
* Resolves to `{ answer, cards, web_results, ... }` — `cards[0]` is the lead card, carrying
|
|
371
130
|
* the chart `image_url`/`embed_url` you can surface in your own UI.
|
|
372
131
|
*/
|
|
373
|
-
declare function takoAnswer(config?:
|
|
132
|
+
declare function takoAnswer(config?: TakoAnswerConfig): Tool<{
|
|
374
133
|
query: string;
|
|
375
134
|
}, TakoAnswerResult>;
|
|
376
135
|
|
|
@@ -378,13 +137,16 @@ declare function takoAnswer(config?: TakoRetrievalConfig): Tool<{
|
|
|
378
137
|
* Download the data behind a result URL: a Tako card's CSV or a web page's text.
|
|
379
138
|
*
|
|
380
139
|
* `mode` sets the delivery, and is reflected in the tool description the model reads:
|
|
381
|
-
* - `"url"`
|
|
140
|
+
* - `"url"` — a short-lived presigned download url. Use when handing a
|
|
382
141
|
* download/embed link to a user, or for large data you won't read yourself.
|
|
383
142
|
* - `"inline"` — the content in the response body, so the model can read and reason
|
|
384
143
|
* over the numbers directly.
|
|
144
|
+
*
|
|
145
|
+
* Left unset, the API chooses and the description tells the model to read the response.
|
|
146
|
+
* Set `mode` to pin the delivery and tell the model which one to expect.
|
|
385
147
|
*/
|
|
386
148
|
declare function takoContents(config?: TakoContentsConfig): Tool<{
|
|
387
149
|
url: string;
|
|
388
150
|
}, TakoContentsResult>;
|
|
389
151
|
|
|
390
|
-
export { type TakoAnswerResponse, type TakoAnswerResult, type TakoBaseConfig, type TakoCard, type TakoCardNode, type TakoCardSource, type
|
|
152
|
+
export { type TakoAnswerConfig, type TakoAnswerResponse, type TakoAnswerResult, type TakoBaseConfig, type TakoCard, type TakoCardNode, type TakoCardSource, type TakoColumnDescriptor, type TakoContentFormat, type TakoContentItem, type TakoContentsConfig, type TakoContentsMode, type TakoContentsResponse, type TakoContentsResult, type TakoDataFreshness, type TakoDataSourceOptions, type TakoDataset, type TakoDatasetCell, type TakoDatasetColumn, type TakoDatasetColumnType, type TakoDatasetSource, type TakoExportPricing, type TakoGraphNodeType, type TakoKnowledgeCardMethodology, type TakoKnowledgeCardRelevance, type TakoMetricDefinition, type TakoResultContent, type TakoRetrievalConfig, type TakoSearchEffort, type TakoSearchResponse, type TakoSearchResult, type TakoSourceIndex, type TakoSources, type TakoUsage, type TakoUsageCompute, type TakoUsageData, type TakoWebCategory, type TakoWebResult, type TakoWebSourceOptions, takoAnswer, takoContents, takoSearch };
|
package/dist/index.js
CHANGED
|
@@ -3,33 +3,28 @@ import { tool } from "ai";
|
|
|
3
3
|
import { z } from "zod";
|
|
4
4
|
|
|
5
5
|
// src/client.ts
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
6
|
+
import { Tako } from "tako-sdk";
|
|
7
|
+
|
|
8
|
+
// src/errors.ts
|
|
9
|
+
import { FetchError, ResponseError } from "tako-sdk";
|
|
10
|
+
function isResponseError(error) {
|
|
11
|
+
return error instanceof ResponseError || error instanceof Error && error.name === "ResponseError" && "response" in error;
|
|
12
|
+
}
|
|
13
|
+
function isFetchError(error) {
|
|
14
|
+
return error instanceof FetchError || error instanceof Error && error.name === "FetchError" && "cause" in error;
|
|
15
|
+
}
|
|
16
|
+
async function wrapTakoError(error, operation) {
|
|
17
|
+
const prefix = `Failed to ${operation} with Tako: `;
|
|
18
|
+
if (isResponseError(error)) {
|
|
19
|
+
const body = await error.response.text().catch(() => "");
|
|
20
|
+
return new Error(`${prefix}Tako API error: ${error.response.status} - ${body}`, { cause: error });
|
|
12
21
|
}
|
|
13
|
-
|
|
14
|
-
const
|
|
15
|
-
|
|
16
|
-
headers: {
|
|
17
|
-
"Content-Type": "application/json",
|
|
18
|
-
"X-API-Key": apiKey
|
|
19
|
-
},
|
|
20
|
-
body: JSON.stringify(body)
|
|
21
|
-
});
|
|
22
|
-
if (!response.ok) {
|
|
23
|
-
const errorText = await response.text();
|
|
24
|
-
throw new Error(`Tako API error: ${response.status} - ${errorText}`);
|
|
25
|
-
}
|
|
26
|
-
return await response.json();
|
|
27
|
-
} catch (error) {
|
|
28
|
-
if (error instanceof Error) {
|
|
29
|
-
throw new Error(`Failed to ${operation} with Tako: ${error.message}`);
|
|
30
|
-
}
|
|
31
|
-
throw error;
|
|
22
|
+
if (isFetchError(error)) {
|
|
23
|
+
const cause = error.cause instanceof Error ? error.cause.message : String(error.cause);
|
|
24
|
+
return new Error(`${prefix}${cause}`, { cause: error });
|
|
32
25
|
}
|
|
26
|
+
if (error instanceof Error) return new Error(`${prefix}${error.message}`, { cause: error });
|
|
27
|
+
return new Error(`${prefix}${String(error)}`, { cause: error });
|
|
33
28
|
}
|
|
34
29
|
|
|
35
30
|
// src/request.ts
|
|
@@ -40,76 +35,17 @@ function resolveApiKey(config) {
|
|
|
40
35
|
function resolveBaseUrl(config) {
|
|
41
36
|
return (config.baseUrl ?? DEFAULT_BASE_URL).replace(/\/+$/, "");
|
|
42
37
|
}
|
|
43
|
-
function buildWebSourceSettings(o) {
|
|
44
|
-
const body = {};
|
|
45
|
-
if (o.count !== void 0) body.count = o.count;
|
|
46
|
-
if (o.includeContents !== void 0) body.include_contents = o.includeContents;
|
|
47
|
-
if (o.category !== void 0) body.category = o.category;
|
|
48
|
-
if (o.includeDomains !== void 0) body.include_domains = o.includeDomains;
|
|
49
|
-
if (o.excludeDomains !== void 0) body.exclude_domains = o.excludeDomains;
|
|
50
|
-
if (o.snippetMaxChars !== void 0) body.snippet_max_chars = o.snippetMaxChars;
|
|
51
|
-
if (o.articleContentMaxChars !== void 0) {
|
|
52
|
-
body.article_content_max_chars = o.articleContentMaxChars;
|
|
53
|
-
}
|
|
54
|
-
if (o.publishedAfter !== void 0) body.published_after = o.publishedAfter;
|
|
55
|
-
if (o.publishedBefore !== void 0) body.published_before = o.publishedBefore;
|
|
56
|
-
return body;
|
|
57
|
-
}
|
|
58
|
-
function assertValidDataSourceOptions(o) {
|
|
59
|
-
if (o.strict && !o.nodeIds?.length) {
|
|
60
|
-
throw new Error(
|
|
61
|
-
"strict requires a non-empty nodeIds. Add node ids from the /v1/graph endpoints, or set strict to false."
|
|
62
|
-
);
|
|
63
|
-
}
|
|
64
|
-
}
|
|
65
|
-
function assertValidRetrievalConfig(config) {
|
|
66
|
-
const dataSource = config.sources?.data ?? config.sources?.tako;
|
|
67
|
-
if (dataSource) assertValidDataSourceOptions(dataSource);
|
|
68
|
-
}
|
|
69
|
-
function buildDataSourceSettings(o) {
|
|
70
|
-
assertValidDataSourceOptions(o);
|
|
71
|
-
const body = {};
|
|
72
|
-
if (o.count !== void 0) body.count = o.count;
|
|
73
|
-
if (o.includeContents !== void 0) body.include_contents = o.includeContents;
|
|
74
|
-
if (o.mode !== void 0) body.mode = o.mode;
|
|
75
|
-
if (o.contentFormat !== void 0) body.content_format = o.contentFormat;
|
|
76
|
-
if (o.nodeIds !== void 0) body.node_ids = o.nodeIds;
|
|
77
|
-
if (o.strict !== void 0) body.strict = o.strict;
|
|
78
|
-
return body;
|
|
79
|
-
}
|
|
80
|
-
function buildGeoLocation(o) {
|
|
81
|
-
return { latitude: o.latitude, longitude: o.longitude };
|
|
82
|
-
}
|
|
83
|
-
function buildOutputSettings(o) {
|
|
84
|
-
const body = {};
|
|
85
|
-
if (o.imageDarkMode !== void 0) body.image_dark_mode = o.imageDarkMode;
|
|
86
|
-
if (o.forceRefresh !== void 0) body.force_refresh = o.forceRefresh;
|
|
87
|
-
return body;
|
|
88
|
-
}
|
|
89
38
|
function buildSearchRequestBody(config, query) {
|
|
90
|
-
const
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
const dataSource = config.sources.data ?? config.sources.tako;
|
|
97
|
-
if (dataSource) sources.data = buildDataSourceSettings(dataSource);
|
|
98
|
-
if (config.sources.web) sources.web = buildWebSourceSettings(config.sources.web);
|
|
99
|
-
body.sources = sources;
|
|
100
|
-
}
|
|
101
|
-
if (config.location !== void 0) body.location = buildGeoLocation(config.location);
|
|
102
|
-
if (config.timezone !== void 0) body.timezone = config.timezone;
|
|
103
|
-
if (config.outputSettings) body.output_settings = buildOutputSettings(config.outputSettings);
|
|
104
|
-
return body;
|
|
39
|
+
const { apiKey: _apiKey, baseUrl: _baseUrl, ...request } = config;
|
|
40
|
+
return { ...request, query };
|
|
41
|
+
}
|
|
42
|
+
function buildAnswerRequestBody(config, query) {
|
|
43
|
+
const { apiKey: _apiKey, baseUrl: _baseUrl, ...request } = config;
|
|
44
|
+
return { ...request, query };
|
|
105
45
|
}
|
|
106
46
|
function buildContentsRequestBody(url, config) {
|
|
107
|
-
const
|
|
108
|
-
|
|
109
|
-
if (config.maxRows !== void 0) body.max_rows = config.maxRows;
|
|
110
|
-
if (config.maxChars !== void 0) body.max_chars = config.maxChars;
|
|
111
|
-
if (config.quoteOnly !== void 0) body.quote_only = config.quoteOnly;
|
|
112
|
-
return body;
|
|
47
|
+
const { apiKey: _apiKey, baseUrl: _baseUrl, ...request } = config;
|
|
48
|
+
return { ...request, url };
|
|
113
49
|
}
|
|
114
50
|
function normalizeSearchResult(response) {
|
|
115
51
|
return {
|
|
@@ -129,9 +65,29 @@ function normalizeContentsResult(response) {
|
|
|
129
65
|
return { ...response, contents: response.contents ?? [] };
|
|
130
66
|
}
|
|
131
67
|
|
|
68
|
+
// src/client.ts
|
|
69
|
+
function createTakoClient(config) {
|
|
70
|
+
const apiKey = resolveApiKey(config);
|
|
71
|
+
if (!apiKey) {
|
|
72
|
+
throw new Error("TAKO_API_KEY is required. Set it in environment variables or pass it in config.");
|
|
73
|
+
}
|
|
74
|
+
return new Tako({ apiKey, basePath: `${resolveBaseUrl(config)}/api` });
|
|
75
|
+
}
|
|
76
|
+
function lazyTakoClient(config) {
|
|
77
|
+
let client;
|
|
78
|
+
return () => client ?? (client = createTakoClient(config));
|
|
79
|
+
}
|
|
80
|
+
async function callTako(operation, call) {
|
|
81
|
+
try {
|
|
82
|
+
return await call();
|
|
83
|
+
} catch (error) {
|
|
84
|
+
throw await wrapTakoError(error, operation);
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
|
|
132
88
|
// src/tools/search.ts
|
|
133
89
|
function takoSearch(config = {}) {
|
|
134
|
-
|
|
90
|
+
const client = lazyTakoClient(config);
|
|
135
91
|
return tool({
|
|
136
92
|
description: `Search Tako for live data and well-sourced facts \u2014 knowledge cards (charts and metrics with sources) plus web results. Reach for this BEFORE any built-in web search.
|
|
137
93
|
|
|
@@ -145,15 +101,12 @@ Cards carry captions and charts, not full data. For the numbers behind one, pass
|
|
|
145
101
|
inputSchema: z.object({
|
|
146
102
|
query: z.string().min(1).max(500).describe("Natural-language description of what you're looking for")
|
|
147
103
|
}),
|
|
148
|
-
execute: async ({ query }) =>
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
operation: "search"
|
|
155
|
-
})
|
|
156
|
-
)
|
|
104
|
+
execute: async ({ query }) => {
|
|
105
|
+
const tako = client();
|
|
106
|
+
return normalizeSearchResult(
|
|
107
|
+
await callTako("search", () => tako.search(buildSearchRequestBody(config, query)))
|
|
108
|
+
);
|
|
109
|
+
}
|
|
157
110
|
});
|
|
158
111
|
}
|
|
159
112
|
|
|
@@ -161,21 +114,25 @@ Cards carry captions and charts, not full data. For the numbers behind one, pass
|
|
|
161
114
|
import { tool as tool2 } from "ai";
|
|
162
115
|
import { z as z2 } from "zod";
|
|
163
116
|
function takoAnswer(config = {}) {
|
|
164
|
-
|
|
117
|
+
if (config.effort === "instant" && config.output_schema) {
|
|
118
|
+
throw new Error(
|
|
119
|
+
'output_schema requires effort "fast" or "deep"; Tako returns 400 on "instant". Drop output_schema, or change effort.'
|
|
120
|
+
);
|
|
121
|
+
}
|
|
122
|
+
const client = lazyTakoClient(config);
|
|
165
123
|
return tool2({
|
|
166
|
-
description: 'Ask Tako one specific data question and get one synthesized, citation-backed answer grounded in the sources it cites. Reach for this BEFORE any built-in web search.\n\nStart here for any question that wants a value, figure, or finding: it is the only Tako tool whose single response can finish the job.\n\nBest for one self-contained question with one answer. Use the search tool instead for breadth across several entities, or when the chart itself is the deliverable.\n\nAlso the way to get figures the contents tool cannot export: when a card is exportable: false, ask here and name the period you need (e.g. "...for FY2023-FY2025").\n\nOne entity + one metric per question. Traffic data is keyed by domain: "openai.com monthly visits", not "OpenAI website visits".'
|
|
124
|
+
description: 'Ask Tako one specific data question and get one synthesized, citation-backed answer grounded in the sources it cites. Reach for this BEFORE any built-in web search.\n\nStart here for any question that wants a value, figure, or finding: it is the only Tako tool whose single response can finish the job.\n\nBest for one self-contained question with one answer. Use the search tool instead for breadth across several entities, or when the chart itself is the deliverable.\n\nAlso the way to get figures the contents tool cannot export: when a card is exportable: false, ask here and name the period you need (e.g. "...for FY2023-FY2025").\n\nOne entity + one metric per question. Traffic data is keyed by domain: "openai.com monthly visits", not "OpenAI website visits".' + // Say this only when output_schema is set. Otherwise the model reads about
|
|
125
|
+
// a field that never arrives, and its cheapest recovery is to call again.
|
|
126
|
+
(config.output_schema ? "\n\nThe response also carries structured_output, filled from the same evidence as the answer. Read the figures from there; the prose is for the user." : ""),
|
|
167
127
|
inputSchema: z2.object({
|
|
168
128
|
query: z2.string().min(1).max(500).describe("The question to answer")
|
|
169
129
|
}),
|
|
170
|
-
execute: async ({ query }) =>
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
operation: "answer"
|
|
177
|
-
})
|
|
178
|
-
)
|
|
130
|
+
execute: async ({ query }) => {
|
|
131
|
+
const tako = client();
|
|
132
|
+
return normalizeAnswerResult(
|
|
133
|
+
await callTako("answer", () => tako.answer(buildAnswerRequestBody(config, query)))
|
|
134
|
+
);
|
|
135
|
+
}
|
|
179
136
|
});
|
|
180
137
|
}
|
|
181
138
|
|
|
@@ -183,27 +140,24 @@ function takoAnswer(config = {}) {
|
|
|
183
140
|
import { tool as tool3 } from "ai";
|
|
184
141
|
import { z as z3 } from "zod";
|
|
185
142
|
function takoContents(config = {}) {
|
|
186
|
-
const
|
|
143
|
+
const client = lazyTakoClient(config);
|
|
187
144
|
return tool3({
|
|
188
|
-
description: "Fetch the real data behind a result url \u2014 a Tako card's webpage_url yields its rows; any other url (a web result's) yields the page's full extracted text. Only call this on a url returned by a prior search or answer call, which gives you a caption and a chart but not the rows.\n\n" + // `
|
|
145
|
+
description: "Fetch the real data behind a result url \u2014 a Tako card's webpage_url yields its rows; any other url (a web result's) yields the page's full extracted text. Only call this on a url returned by a prior search or answer call, which gives you a caption and a chart but not the rows.\n\n" + // `quote_only` outranks `mode`: the API ignores mode on a quote and returns
|
|
189
146
|
// null for url and every payload field. Describing either delivery here
|
|
190
147
|
// would promise content that never arrives, and the model's cheapest
|
|
191
148
|
// recovery from an unexplained null is to call again.
|
|
192
|
-
(config.
|
|
149
|
+
(config.quote_only ? "Configured for price quotes only: returns the export cost and rate card, and NO content. The url and data fields are always null and the call is free \u2014 report the price, and do not call again expecting rows.\n\n" : config.mode === "inline" ? "Returns the content in the response body \u2014 read and compute over the numbers directly.\n\n" : config.mode === "url" ? "Returns a short-lived presigned download url, NOT the data itself: surface the link, do not parse it or call again expecting rows.\n\n" : "Delivery follows the API's own default, so read the response rather than assuming: if the item carries a url and no content, surface the link and do not parse it; if it carries content, read and compute over it directly.\n\n") + "Only cards whose exportable field is true can be downloaded; a non-exportable card always returns 403 and retrying will not change that \u2014 get its figures from the answer tool instead, naming the period you need. Web urls always work, so this is also the fallback when a search surfaced a relevant web result but no fitting data card.\n\nOn each returned item, content_format names the serialization for card data and is null or absent for web page text; total_rows and truncated tell you whether the card had more rows than were returned.",
|
|
193
150
|
inputSchema: z3.object({
|
|
194
151
|
// Validated as a url so a malformed value fails here, with a message the
|
|
195
152
|
// model can act on, instead of costing a priced round trip to the API.
|
|
196
153
|
url: z3.url().describe("A TakoCard.webpage_url or WebResult.url to download contents for")
|
|
197
154
|
}),
|
|
198
|
-
execute: async ({ url }) =>
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
operation: "fetch contents"
|
|
205
|
-
})
|
|
206
|
-
)
|
|
155
|
+
execute: async ({ url }) => {
|
|
156
|
+
const tako = client();
|
|
157
|
+
return normalizeContentsResult(
|
|
158
|
+
await callTako("fetch contents", () => tako.contents(buildContentsRequestBody(url, config)))
|
|
159
|
+
);
|
|
160
|
+
}
|
|
207
161
|
});
|
|
208
162
|
}
|
|
209
163
|
export {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@takoviz/ai-sdk",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "4.0.0",
|
|
4
4
|
"description": "Tako knowledge search, answer, and contents tools for the Vercel AI SDK",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.js",
|
|
@@ -42,24 +42,25 @@
|
|
|
42
42
|
"devDependencies": {
|
|
43
43
|
"@ai-sdk/openai": "^4.0.0",
|
|
44
44
|
"@types/node": "^24.10.1",
|
|
45
|
+
"@types/semver": "^7.8.0",
|
|
45
46
|
"ai": "^7.0.0",
|
|
46
|
-
"
|
|
47
|
-
"ajv-formats": "^3.0.1",
|
|
48
|
-
"tako-sdk": "^1.1.10",
|
|
47
|
+
"semver": "^7.8.5",
|
|
49
48
|
"tsup": "^8.5.0",
|
|
50
49
|
"tsx": "^4.20.6",
|
|
51
50
|
"typescript": "^5.9.3",
|
|
52
|
-
"vitest": "^3.0.0"
|
|
53
|
-
|
|
51
|
+
"vitest": "^3.0.0"
|
|
52
|
+
},
|
|
53
|
+
"dependencies": {
|
|
54
|
+
"tako-sdk": "^1.3.0"
|
|
54
55
|
},
|
|
55
56
|
"scripts": {
|
|
56
57
|
"build": "tsup src/index.ts --format esm --dts",
|
|
57
58
|
"typecheck": "tsc --noEmit -p tsconfig.check.json",
|
|
58
59
|
"test": "vitest run",
|
|
59
60
|
"test:watch": "vitest",
|
|
60
|
-
"test:
|
|
61
|
+
"test:live": "vitest run --config vitest.live.config.ts",
|
|
61
62
|
"test:package": "node scripts/verify-package.mjs",
|
|
62
|
-
"lint:package": "npx -y publint@latest --strict --pack npm && npx -y @arethetypeswrong/cli@latest --pack . --ignore-rules cjs-resolves-to-esm",
|
|
63
|
-
"
|
|
63
|
+
"lint:package": "npm run --silent build && npx -y publint@latest --strict --pack npm && npx -y @arethetypeswrong/cli@latest --pack . --ignore-rules cjs-resolves-to-esm",
|
|
64
|
+
"check:sdk-lag": "tsx scripts/check-sdk-lag.ts"
|
|
64
65
|
}
|
|
65
66
|
}
|