@dromney/mapthis 0.1.0 → 0.2.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 +108 -27
- package/dist/ai/index.cjs +105 -1
- package/dist/ai/index.cjs.map +1 -1
- package/dist/ai/index.d.cts +9 -9
- package/dist/ai/index.d.ts +9 -9
- package/dist/ai/index.js +101 -2
- package/dist/ai/index.js.map +1 -1
- package/dist/{domain-Dc1wSTkf.d.cts → domain-CL4ro2YW.d.cts} +1 -1
- package/dist/{domain-CZ-L-ntu.d.ts → domain-Dh8cPBVp.d.ts} +1 -1
- package/dist/generate/index.cjs +1 -1
- package/dist/generate/index.cjs.map +1 -1
- package/dist/generate/index.d.cts +4 -4
- package/dist/generate/index.d.ts +4 -4
- package/dist/generate/index.js +1 -1
- package/dist/generate/index.js.map +1 -1
- package/dist/geocoding/index.d.cts +3 -3
- package/dist/geocoding/index.d.ts +3 -3
- package/dist/index.cjs +1 -1
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +2 -2
- package/dist/index.d.ts +2 -2
- package/dist/index.js +1 -1
- package/dist/index.js.map +1 -1
- package/dist/parser-CrG1A6dD.d.ts +176 -0
- package/dist/parser-DsIwIsHK.d.cts +176 -0
- package/dist/react/index.d.cts +197 -0
- package/dist/react/index.d.ts +197 -0
- package/dist/{schemas-Dy5coqXo.d.cts → schemas-CZao45EU.d.cts} +1 -1
- package/dist/{schemas-Dy5coqXo.d.ts → schemas-CZao45EU.d.ts} +1 -1
- package/dist/types/index.cjs +1 -1
- package/dist/types/index.cjs.map +1 -1
- package/dist/types/index.d.cts +2 -2
- package/dist/types/index.d.ts +2 -2
- package/dist/types/index.js +1 -1
- package/dist/types/index.js.map +1 -1
- package/dist/{types-BhqKlq0k.d.ts → types-BhPqZwOj.d.ts} +2 -2
- package/dist/{types-rFjK5YcJ.d.cts → types-C-xqh9Sh.d.cts} +2 -2
- package/dist/utils/index.d.cts +1 -1
- package/dist/utils/index.d.ts +1 -1
- package/package.json +10 -6
- package/dist/parser-CzXzpmVv.d.cts +0 -111
- package/dist/parser-N7-fNxeu.d.ts +0 -111
package/dist/ai/index.js
CHANGED
|
@@ -2,6 +2,7 @@ import { z } from 'zod';
|
|
|
2
2
|
import { convert } from 'html-to-text';
|
|
3
3
|
import { encodingForModel, getEncoding } from 'js-tiktoken';
|
|
4
4
|
import OpenAI from 'openai';
|
|
5
|
+
import Anthropic from '@anthropic-ai/sdk';
|
|
5
6
|
|
|
6
7
|
// src/types/errors.ts
|
|
7
8
|
var MapthisError = class extends Error {
|
|
@@ -431,10 +432,108 @@ function createOpenAiBackend(config) {
|
|
|
431
432
|
}
|
|
432
433
|
};
|
|
433
434
|
}
|
|
435
|
+
var DEFAULT_ANTHROPIC_MODEL = "claude-haiku-4-5-20251001";
|
|
436
|
+
var DEFAULT_ANTHROPIC_TOKEN_LIMIT = 2e5;
|
|
437
|
+
var DEFAULT_ANTHROPIC_MAX_OUTPUT_TOKENS = 4096;
|
|
438
|
+
function resolveEncoding2(model) {
|
|
439
|
+
try {
|
|
440
|
+
return encodingForModel(model);
|
|
441
|
+
} catch {
|
|
442
|
+
return getEncoding("cl100k_base");
|
|
443
|
+
}
|
|
444
|
+
}
|
|
445
|
+
function createAnthropicBackend(config) {
|
|
446
|
+
const model = config.model ?? DEFAULT_ANTHROPIC_MODEL;
|
|
447
|
+
const tokenLimit = config.tokenLimit ?? DEFAULT_ANTHROPIC_TOKEN_LIMIT;
|
|
448
|
+
const maxOutputTokens = config.maxOutputTokens ?? DEFAULT_ANTHROPIC_MAX_OUTPUT_TOKENS;
|
|
449
|
+
const client = new Anthropic({
|
|
450
|
+
apiKey: config.apiKey,
|
|
451
|
+
...config.baseURL ? { baseURL: config.baseURL } : {}
|
|
452
|
+
});
|
|
453
|
+
const encoding = resolveEncoding2(model);
|
|
454
|
+
return {
|
|
455
|
+
model,
|
|
456
|
+
tokenLimit,
|
|
457
|
+
countTokens(text) {
|
|
458
|
+
return encoding.encode(text).length;
|
|
459
|
+
},
|
|
460
|
+
async chatCompletion(content) {
|
|
461
|
+
const response = await client.messages.create({
|
|
462
|
+
model,
|
|
463
|
+
max_tokens: maxOutputTokens,
|
|
464
|
+
messages: [{ role: "user", content }]
|
|
465
|
+
});
|
|
466
|
+
const text = response.content.filter((b) => b.type === "text").map((b) => b.text).join("");
|
|
467
|
+
return {
|
|
468
|
+
output: text,
|
|
469
|
+
inputTokens: response.usage.input_tokens,
|
|
470
|
+
outputTokens: response.usage.output_tokens
|
|
471
|
+
};
|
|
472
|
+
},
|
|
473
|
+
async chatFunctionJson(systemMessage, userMessage, schema, functionName) {
|
|
474
|
+
try {
|
|
475
|
+
const response = await client.messages.create({
|
|
476
|
+
model,
|
|
477
|
+
max_tokens: maxOutputTokens,
|
|
478
|
+
system: systemMessage,
|
|
479
|
+
tools: [
|
|
480
|
+
{
|
|
481
|
+
name: functionName,
|
|
482
|
+
description: `Return structured output matching the ${functionName} schema.`,
|
|
483
|
+
input_schema: schema
|
|
484
|
+
}
|
|
485
|
+
],
|
|
486
|
+
tool_choice: { type: "tool", name: functionName },
|
|
487
|
+
messages: [{ role: "user", content: userMessage }]
|
|
488
|
+
});
|
|
489
|
+
const toolUse = response.content.find(
|
|
490
|
+
(b) => b.type === "tool_use"
|
|
491
|
+
);
|
|
492
|
+
if (!toolUse) {
|
|
493
|
+
throw new AiResponseJsonError("No tool use in Anthropic response");
|
|
494
|
+
}
|
|
495
|
+
if (response.stop_reason === "max_tokens") {
|
|
496
|
+
throw new AiOutputLengthError(
|
|
497
|
+
`Anthropic hit max_tokens (${maxOutputTokens}) before completing tool call`
|
|
498
|
+
);
|
|
499
|
+
}
|
|
500
|
+
return {
|
|
501
|
+
output: toolUse.input,
|
|
502
|
+
inputTokens: response.usage.input_tokens,
|
|
503
|
+
outputTokens: response.usage.output_tokens
|
|
504
|
+
};
|
|
505
|
+
} catch (error) {
|
|
506
|
+
if (error instanceof AiError) throw error;
|
|
507
|
+
if (error instanceof Anthropic.APIError) {
|
|
508
|
+
const msg = (error.message ?? "").toLowerCase();
|
|
509
|
+
if (msg.includes("prompt is too long") || msg.includes("max_tokens")) {
|
|
510
|
+
throw new AiInputLengthError(`Text too long: ${error.message}`, {
|
|
511
|
+
cause: error
|
|
512
|
+
});
|
|
513
|
+
}
|
|
514
|
+
if (msg.includes("invalid") && msg.includes("schema")) {
|
|
515
|
+
throw new InvalidJsonSchemaError(`Invalid schema: ${error.message}`, {
|
|
516
|
+
cause: error
|
|
517
|
+
});
|
|
518
|
+
}
|
|
519
|
+
throw new AiError(`Anthropic error: ${error.message}`, { cause: error });
|
|
520
|
+
}
|
|
521
|
+
throw new AiError("Anthropic error (unknown)", {
|
|
522
|
+
cause: error instanceof Error ? error : void 0
|
|
523
|
+
});
|
|
524
|
+
}
|
|
525
|
+
}
|
|
526
|
+
};
|
|
527
|
+
}
|
|
434
528
|
|
|
435
529
|
// src/ai/parser.ts
|
|
530
|
+
function resolveBackend(config) {
|
|
531
|
+
if ("backend" in config) return config.backend;
|
|
532
|
+
if (config.provider === "anthropic") return createAnthropicBackend(config);
|
|
533
|
+
return createOpenAiBackend(config);
|
|
534
|
+
}
|
|
436
535
|
function createLocationParser(config) {
|
|
437
|
-
const backend =
|
|
536
|
+
const backend = resolveBackend(config);
|
|
438
537
|
return {
|
|
439
538
|
parseLocationsFromText: (text) => parseLocationsFromText(backend, text),
|
|
440
539
|
parseLocationsFromHtml: (html) => parseLocationsFromHtml(backend, html),
|
|
@@ -442,6 +541,6 @@ function createLocationParser(config) {
|
|
|
442
541
|
};
|
|
443
542
|
}
|
|
444
543
|
|
|
445
|
-
export { AiError, AiInputLengthError, AiOutputLengthError, AiResponseJsonError, DEFAULT_MODEL, DEFAULT_TOKEN_LIMIT, InvalidJsonSchemaError, LOCATIONS_FUNCTIONS_NAME, LOCATIONS_MAX_OUTPUT_TOKENS, LOCATIONS_PROMPT_VERSION, LOCATIONS_SCHEMA, LOCATIONS_SYSTEM_MESSAGE, NoLocationsFoundError, SUMMARIZATION_PROMPT, SUMMARIZE_PROMPT_VERSION, SummarizeTextError, createLocationParser, createOpenAiBackend, parseLocationsFromHtml, parseLocationsFromText, parseLocationsFromUrl, summarizeText };
|
|
544
|
+
export { AiError, AiInputLengthError, AiOutputLengthError, AiResponseJsonError, DEFAULT_ANTHROPIC_MAX_OUTPUT_TOKENS, DEFAULT_ANTHROPIC_MODEL, DEFAULT_ANTHROPIC_TOKEN_LIMIT, DEFAULT_MODEL, DEFAULT_TOKEN_LIMIT, InvalidJsonSchemaError, LOCATIONS_FUNCTIONS_NAME, LOCATIONS_MAX_OUTPUT_TOKENS, LOCATIONS_PROMPT_VERSION, LOCATIONS_SCHEMA, LOCATIONS_SYSTEM_MESSAGE, NoLocationsFoundError, SUMMARIZATION_PROMPT, SUMMARIZE_PROMPT_VERSION, SummarizeTextError, createAnthropicBackend, createLocationParser, createOpenAiBackend, parseLocationsFromHtml, parseLocationsFromText, parseLocationsFromUrl, summarizeText };
|
|
446
545
|
//# sourceMappingURL=index.js.map
|
|
447
546
|
//# sourceMappingURL=index.js.map
|
package/dist/ai/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/types/errors.ts","../../src/ai/errors.ts","../../src/ai/prompts.ts","../../src/scrape/errors.ts","../../src/scrape/html.ts","../../src/scrape/text.ts","../../src/ai/summarize.ts","../../src/ai/locations.ts","../../src/ai/openai.ts","../../src/ai/parser.ts"],"names":[],"mappings":"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","file":"index.js","sourcesContent":["/**\n * Base error class for every error thrown by the mapthis package.\n *\n * All feature-specific error classes (scrape, search, ai, geocoding) extend\n * this, so consumers can do `catch (e) { if (e instanceof MapthisError) ... }`\n * to distinguish library errors from other exceptions.\n */\nexport class MapthisError extends Error {\n constructor(message?: string, options?: ErrorOptions) {\n super(message, options)\n this.name = \"MapthisError\"\n }\n}\n","import { MapthisError } from \"../types/errors\"\n\nexport class AiError extends MapthisError {\n constructor(message?: string, options?: ErrorOptions) {\n super(message, options)\n this.name = \"AiError\"\n }\n}\n\n/**\n * The LLM returned zero locations after deduplication.\n */\nexport class NoLocationsFoundError extends AiError {\n constructor(message?: string, options?: ErrorOptions) {\n super(message ?? \"No locations found in input\", options)\n this.name = \"NoLocationsFoundError\"\n }\n}\n\n/**\n * Summarization pipeline failed (one of the chunk calls rejected).\n */\nexport class SummarizeTextError extends AiError {\n constructor(message?: string, options?: ErrorOptions) {\n super(message, options)\n this.name = \"SummarizeTextError\"\n }\n}\n\n/**\n * A JSON schema passed to the LLM was rejected as invalid by the provider.\n */\nexport class InvalidJsonSchemaError extends AiError {\n constructor(message?: string, options?: ErrorOptions) {\n super(message, options)\n this.name = \"InvalidJsonSchemaError\"\n }\n}\n\n/**\n * The LLM response could not be parsed as JSON (typically because the output\n * was truncated or malformed).\n */\nexport class AiResponseJsonError extends AiError {\n constructor(message?: string, options?: ErrorOptions) {\n super(message, options)\n this.name = \"AiResponseJsonError\"\n }\n}\n\n/**\n * Input text exceeded the model's context window.\n */\nexport class AiInputLengthError extends AiError {\n constructor(message?: string, options?: ErrorOptions) {\n super(message, options)\n this.name = \"AiInputLengthError\"\n }\n}\n\n/**\n * The LLM hit the output token limit before completing its JSON response.\n */\nexport class AiOutputLengthError extends AiError {\n constructor(message?: string, options?: ErrorOptions) {\n super(message, options)\n this.name = \"AiOutputLengthError\"\n }\n}\n","import type { JSONSchema7Object } from \"json-schema\"\n\n/**\n * Semantic version of the location-extraction system prompt. Stored alongside\n * each place record in the private app so results can be re-run when the\n * prompt is updated. Bump when you change {@link LOCATIONS_SYSTEM_MESSAGE} or\n * {@link LOCATIONS_SCHEMA} in a way that meaningfully changes output.\n */\nexport const LOCATIONS_PROMPT_VERSION = \"0.0.1\"\n\nexport const LOCATIONS_FUNCTIONS_NAME = \"get_places\"\n\nexport const LOCATIONS_SYSTEM_MESSAGE = `You parse a list of place addresses, corresponding brief one-sentence descriptions, and a descriptive title (preferably directly quoted) from text. These addresses might be countries, cities, attractions, parks, bars, etc.\nIf the text seems to be listing places that are all within a single parent place, you don't return the parent place, but you do add it to all the childrens' addresses.\nFor example, for an article about places in greece, you would not return \"greece\", but if the Acropolis was mentioned, you would return \"Acropolis, Athens, Greece\"\n\nYou ignore extraneous text, such as recommended articles, advertisments, etc.\n`\n\nexport const LOCATIONS_SCHEMA: JSONSchema7Object = {\n type: \"object\",\n required: [\"locations\", \"title\"],\n properties: {\n locations: {\n type: \"array\",\n items: {\n type: \"object\",\n required: [\"address\", \"description\"],\n properties: {\n address: { type: \"string\" },\n description: { type: \"string\" },\n },\n },\n },\n title: { type: \"string\" },\n },\n}\n\nexport const LOCATIONS_MAX_OUTPUT_TOKENS = 500\n\n/**\n * Semantic version of the summarization prompt. Bump when meaningful changes\n * are made to {@link SUMMARIZATION_PROMPT}.\n */\nexport const SUMMARIZE_PROMPT_VERSION = \"0.0.1\"\n\nexport const SUMMARIZATION_PROMPT = `Summarize the given text. The summary must reduce the number of characters from the original text by at least a factor of {RATIO}.\nFor example, text of 1000 characters must output a summary less than {EXAMPLE} characters.\nThis summary will be used to extract a list of locations and addresses, so keep any text that seems to be a list of locations/places, a location and paragraph describing it, or otherwise clearly a location name or address.\nHere is the text:\n`\n","import { MapthisError } from \"../types/errors\"\n\nexport class ScrapeError extends MapthisError {\n constructor(message?: string, options?: ErrorOptions) {\n super(message, options)\n this.name = \"ScrapeError\"\n }\n}\n\n/**\n * Thrown when a provided URL fails validation or cannot be parsed by `fetch`.\n */\nexport class InvalidUrlError extends ScrapeError {\n constructor(message?: string, options?: ErrorOptions) {\n super(message, options)\n this.name = \"InvalidUrlError\"\n }\n}\n\n/**\n * Thrown when a remote server returns 401 or 403 for an HTML fetch, meaning\n * the page does not permit anonymous scraping.\n */\nexport class HtmlUnauthorizedError extends ScrapeError {\n constructor(message?: string, options?: ErrorOptions) {\n super(message, options)\n this.name = \"HtmlUnauthorizedError\"\n }\n}\n\n/**\n * Thrown when the underlying `html-to-text` conversion fails.\n */\nexport class HtmlToTextError extends ScrapeError {\n constructor(message?: string, options?: ErrorOptions) {\n super(message, options)\n this.name = \"HtmlToTextError\"\n }\n}\n","import { z } from \"zod\"\nimport { HtmlUnauthorizedError, InvalidUrlError, ScrapeError } from \"./errors\"\n\nconst withHttps = (url: string): string => {\n const hasHttp = /^http?:\\/\\//i.test(url)\n const hasHttps = /^https?:\\/\\//i.test(url)\n if (!hasHttp && !hasHttps) return `https://${url}`\n return url\n}\n\n/**\n * Fetch the raw HTML of a URL, performing light validation first.\n *\n * - URLs without a scheme are prefixed with `https://`.\n * - URL shape is validated via Zod before the network call.\n * - 401/403 responses throw {@link HtmlUnauthorizedError}.\n * - Other non-OK responses throw {@link ScrapeError} with the status text.\n * - `TypeError` from `fetch` (typically malformed URL) is rethrown as\n * {@link InvalidUrlError}.\n *\n * This function does not set any special headers, user agent, or cookies.\n * Sites with aggressive bot-detection may return empty bodies or errors; the\n * caller is responsible for retry / user-agent rotation strategies if needed.\n */\nexport async function getHtmlFromUrl(url: string): Promise<string> {\n const urlFixed = withHttps(url.toLowerCase())\n const isUrl = z.string().url().safeParse(urlFixed)\n if (!isUrl.success) throw new InvalidUrlError(`Invalid URL: ${url}`)\n\n try {\n const response = await fetch(urlFixed)\n const html = await response.text()\n if (response.status === 403 || response.status === 401) {\n throw new HtmlUnauthorizedError(\n `${response.status} Not authorized to scrape this website`,\n )\n }\n if (!response.ok) {\n throw new ScrapeError(\n `Bad response when getting HTML. Status: ${response.status} ${response.statusText}`,\n )\n }\n return html\n } catch (error) {\n if (error instanceof TypeError) {\n throw new InvalidUrlError(`Invalid URL. Original error: ${error.message}`)\n }\n throw error\n }\n}\n","import { convert } from \"html-to-text\"\nimport { HtmlToTextError } from \"./errors\"\nimport { getHtmlFromUrl } from \"./html\"\n\nconst SKIP_SELECTORS = [\n \"img\",\n \"header\",\n \"footer\",\n \"audio\",\n \"button\",\n \"canvas\",\n \"code\",\n \"nav\",\n \"#nav\",\n \"figure\",\n \"figcaption\",\n \".comment\",\n \".comments\",\n \"#comments\",\n \"#related-posts\",\n \"#related\",\n \".related\",\n \".related-posts\",\n] as const\n\n/**\n * Convert raw HTML to plain text suitable for LLM consumption.\n *\n * The converter is configured to:\n * - strip anchor hrefs when they duplicate the link text;\n * - skip navigation, footer, comments, related-post widgets, images, and\n * other non-content elements;\n * - collapse repeated blank lines and double spaces so token count doesn't\n * balloon from whitespace.\n */\nexport function htmlToText(html: string): string {\n try {\n let out = convert(html, {\n wordwrap: false,\n selectors: [\n {\n selector: \"a\",\n options: {\n ignoreHref: true,\n hideLinkHrefIfSameAsText: true,\n },\n },\n ...SKIP_SELECTORS.map((tag) => ({ selector: tag, format: \"skip\" })),\n ],\n })\n out = out.replaceAll(\"\\r\\n\", \"\\n\")\n // Iteratively collapse blank lines and double spaces. 20 passes is\n // overkill for any realistic input but cheap and bounds worst case.\n for (let i = 0; i < 20; i++) {\n out = out.replaceAll(\"\\n\\n\\n\", \"\\n\\n\")\n out = out.replaceAll(\" \", \" \")\n }\n return out\n } catch (error) {\n if (error instanceof Error) {\n throw new HtmlToTextError(`HTML to text conversion failed: ${error.message}`, {\n cause: error,\n })\n }\n throw new HtmlToTextError(\"HTML to text conversion failed\")\n }\n}\n\n/**\n * Convenience: fetch a URL and convert its HTML to plain text in one call.\n *\n * Equivalent to `htmlToText(await getHtmlFromUrl(url))`.\n */\nexport async function getTextFromUrl(url: string): Promise<string> {\n const html = await getHtmlFromUrl(url)\n return htmlToText(html)\n}\n","import { SummarizeTextError } from \"./errors\"\nimport type { OpenAiBackend } from \"./openai\"\nimport { SUMMARIZATION_PROMPT, SUMMARIZE_PROMPT_VERSION } from \"./prompts\"\nimport type { SummarizedText } from \"./types\"\n\n/**\n * Reduce a large text to a summary that will fit inside the model's context\n * window alongside a known downstream prompt (e.g. the location-extraction\n * call).\n *\n * Algorithm overview\n * ------------------\n * We need to fit both the summary and a downstream \"final\" prompt into the\n * model's context window. Given:\n *\n * - `token_limit` — model context window (with a safety factor)\n * - `text_tokens` — size of the input text\n * - `summarize_prompt_tokens` — size of the summarization prompt itself\n * - `final_prompt_tokens` — size of the downstream prompt\n * - `final_output_tokens` — expected size of the downstream response\n *\n * We split the input into chunks, summarize each in parallel, and concatenate\n * the results. The per-chunk size is chosen so that the combined summary\n * leaves room for the downstream prompt:\n *\n * final_input_tokens = token_limit - final_output_tokens\n * summary_output_tokens = final_input_tokens - final_prompt_tokens\n * num_chunks = ceil((text_tokens + summary_output_tokens)\n * / (token_limit - summarize_prompt_tokens))\n * chunk_output_tokens = summary_output_tokens / num_chunks\n * chunk_input_tokens = token_limit - summarize_prompt_tokens - chunk_output_tokens\n *\n * A safety factor is applied to chunk output sizes because LLMs routinely\n * overshoot explicit length instructions.\n */\nexport async function summarizeText(\n backend: OpenAiBackend,\n text: string,\n finalPrompt = \"\",\n finalOutputTokens = 0,\n chunkOutputSafetyFactor = 0.3,\n tokenLimitSafetyFactor = 0.02,\n): Promise<SummarizedText> {\n const tokenLimit = backend.tokenLimit * (1 - tokenLimitSafetyFactor)\n const textTokens = backend.countTokens(text)\n const summarizePromptTokens = backend.countTokens(SUMMARIZATION_PROMPT)\n const finalPromptTokens = backend.countTokens(finalPrompt)\n\n const finalInputTokens = tokenLimit - finalOutputTokens\n const summaryOutputTokens = finalInputTokens - finalPromptTokens\n\n const numChunks = Math.max(\n 1,\n Math.ceil((textTokens + summaryOutputTokens) / (tokenLimit - summarizePromptTokens)),\n )\n const chunkOutputTokens = Math.floor(\n (summaryOutputTokens / numChunks) * (1 - chunkOutputSafetyFactor),\n )\n const chunkInputTokens = Math.floor(tokenLimit - summarizePromptTokens - chunkOutputTokens)\n const summaryRatio = chunkOutputTokens / chunkInputTokens\n\n // Split the text into chunks that each fit inside `chunkInputTokens`.\n // Lines longer than a single chunk are force-split by token, not by\n // character, so UTF-8 multibyte characters aren't corrupted.\n const textLines = text.split(\"\\n\")\n const chunks: string[] = []\n\n let currentChunk = \"\"\n let currentChunkTokens = 0\n const nextChunk = (): void => {\n if (currentChunk) chunks.push(currentChunk)\n currentChunk = \"\"\n currentChunkTokens = 0\n }\n\n // We need an encoder for the force-split path. The backend's countTokens\n // gives us length but not the encoded array; for force-splitting we only\n // need to chunk by an approximate token count using the same rounding.\n // To keep this function backend-agnostic, we fall back to character-based\n // splitting for over-long lines (rare edge case in practice).\n for (const line of textLines) {\n const numLineTokens = backend.countTokens(line) + 1 // +1 for the newline\n\n if (numLineTokens > chunkInputTokens) {\n // Long line: flush current chunk, then split this line by\n // approximate token ratio into sub-chunks.\n nextChunk()\n const charsPerToken = line.length / Math.max(1, backend.countTokens(line))\n const charsPerChunk = Math.max(1, Math.floor(chunkInputTokens * charsPerToken))\n for (let i = 0; i < line.length; i += charsPerChunk) {\n chunks.push(line.slice(i, i + charsPerChunk))\n }\n continue\n }\n\n if (currentChunkTokens + numLineTokens > chunkInputTokens) {\n nextChunk()\n }\n currentChunkTokens += numLineTokens\n currentChunk += line + \"\\n\"\n }\n nextChunk()\n\n try {\n const ratioText = (Math.ceil(summaryRatio * 10) / 10).toFixed(1)\n const exampleText = Math.ceil(1000 / Math.max(summaryRatio, 0.01)).toFixed(0)\n const summaryPrompt = SUMMARIZATION_PROMPT.replace(\"{RATIO}\", ratioText).replace(\n \"{EXAMPLE}\",\n exampleText,\n )\n\n let inputTokens = 0\n let outputTokens = 0\n const summaries = await Promise.all(\n chunks.map(async (chunk) => {\n const response = await backend.chatCompletion(summaryPrompt + chunk)\n inputTokens += response.inputTokens\n outputTokens += response.outputTokens\n return response.output\n }),\n )\n\n const summarized = summaries.join(\"\\n\")\n const summarizedTokens = backend.countTokens(summarized)\n\n return {\n text,\n textTokens,\n summarized,\n summarizedTokens,\n inputTokens,\n outputTokens,\n chunks: chunks.length,\n summarizePromptVersion: SUMMARIZE_PROMPT_VERSION,\n }\n } catch (error) {\n if (error instanceof Error) {\n throw new SummarizeTextError(`Failed to summarize text: ${error.message}`, {\n cause: error,\n })\n }\n throw new SummarizeTextError(\"Failed to summarize text (unknown error)\")\n }\n}\n","import { getHtmlFromUrl } from \"../scrape/html\"\nimport { htmlToText } from \"../scrape/text\"\nimport type { ParsedLocations } from \"../types/domain\"\nimport { NoLocationsFoundError } from \"./errors\"\nimport type { OpenAiBackend } from \"./openai\"\nimport {\n LOCATIONS_FUNCTIONS_NAME,\n LOCATIONS_MAX_OUTPUT_TOKENS,\n LOCATIONS_PROMPT_VERSION,\n LOCATIONS_SCHEMA,\n LOCATIONS_SYSTEM_MESSAGE,\n} from \"./prompts\"\nimport { summarizeText } from \"./summarize\"\nimport type { GetLocationsResponse } from \"./types\"\n\nconst FINAL_PROMPT = LOCATIONS_SYSTEM_MESSAGE + JSON.stringify(LOCATIONS_SCHEMA) + LOCATIONS_FUNCTIONS_NAME\n\nasync function callLocationExtraction(backend: OpenAiBackend, text: string) {\n const out = await backend.chatFunctionJson<ParsedLocations>(\n LOCATIONS_SYSTEM_MESSAGE,\n text,\n LOCATIONS_SCHEMA,\n LOCATIONS_FUNCTIONS_NAME,\n )\n\n // LLM frequently returns the same address twice across different prompts.\n // Dedupe by address string.\n const seen = new Set<string>()\n const unique = out.output.locations.filter((loc) => {\n if (seen.has(loc.address)) return false\n seen.add(loc.address)\n return true\n })\n if (unique.length === 0) throw new NoLocationsFoundError()\n out.output.locations = unique\n\n return out\n}\n\n/**\n * Parse locations from freeform text. Two-stage: summarize to fit the context\n * window, then a function-calling JSON completion for extraction.\n */\nexport async function parseLocationsFromText(\n backend: OpenAiBackend,\n text: string,\n): Promise<GetLocationsResponse> {\n const {\n summarized,\n inputTokens: sumIn,\n outputTokens: sumOut,\n textTokens,\n summarizedTokens,\n summarizePromptVersion,\n } = await summarizeText(backend, text, FINAL_PROMPT, LOCATIONS_MAX_OUTPUT_TOKENS)\n\n const {\n output,\n inputTokens: locIn,\n outputTokens: locOut,\n } = await callLocationExtraction(backend, summarized)\n\n return {\n output,\n locationInputTokens: locIn,\n locationOutputTokens: locOut,\n summaryInputTokens: sumIn,\n summaryOutputTokens: sumOut,\n inputTokens: sumIn + locIn,\n outputTokens: sumOut + locOut,\n textTokens,\n summarizedTokens,\n summaryPromptVersion: summarizePromptVersion,\n locationPromptVersion: LOCATIONS_PROMPT_VERSION,\n }\n}\n\n/**\n * Parse locations from HTML. Equivalent to `htmlToText` followed by\n * {@link parseLocationsFromText}.\n */\nexport async function parseLocationsFromHtml(\n backend: OpenAiBackend,\n html: string,\n): Promise<GetLocationsResponse> {\n return parseLocationsFromText(backend, htmlToText(html))\n}\n\n/**\n * Parse locations from a URL. Equivalent to fetching the HTML, converting to\n * text, and calling {@link parseLocationsFromText}. Throws any scrape errors\n * unchanged.\n */\nexport async function parseLocationsFromUrl(\n backend: OpenAiBackend,\n url: string,\n): Promise<GetLocationsResponse> {\n const html = await getHtmlFromUrl(url)\n return parseLocationsFromHtml(backend, html)\n}\n","import { encodingForModel, getEncoding, type Tiktoken, type TiktokenModel } from \"js-tiktoken\"\nimport OpenAI from \"openai\"\nimport type { JSONSchema7Object } from \"json-schema\"\nimport {\n AiInputLengthError,\n AiOutputLengthError,\n AiResponseJsonError,\n AiError,\n InvalidJsonSchemaError,\n} from \"./errors\"\nimport type { OpenAIOutput } from \"./types\"\n\nexport const DEFAULT_MODEL = \"gpt-4o-mini\"\nexport const DEFAULT_TOKEN_LIMIT = 128000\n\n/**\n * An OpenAI-backed inference context shared by the summarizer and location\n * parser. Instances are produced by {@link createOpenAiBackend} so API keys\n * and model choices live in the consumer's code, never in module-level state.\n */\nexport interface OpenAiBackend {\n readonly model: string\n readonly tokenLimit: number\n countTokens(text: string): number\n chatCompletion(content: string): Promise<OpenAIOutput<string>>\n chatFunctionJson<T>(\n systemMessage: string,\n userMessage: string,\n schema: JSONSchema7Object,\n functionName: string,\n ): Promise<OpenAIOutput<T>>\n}\n\nexport interface OpenAiBackendConfig {\n /** OpenAI API key. Passed directly to the SDK. */\n apiKey: string\n /** Model name passed to the completions API. Defaults to `gpt-4o-mini`. */\n model?: string\n /** Context-window budget used by the summarizer. Defaults to 128k. */\n tokenLimit?: number\n /**\n * Optional base URL for the OpenAI API. Use this to route through an\n * OpenAI-compatible gateway (Azure, Together, etc.).\n */\n baseURL?: string\n}\n\nfunction resolveEncoding(model: string): Tiktoken {\n try {\n return encodingForModel(model as TiktokenModel)\n } catch {\n // Fallback for models tiktoken does not recognize by name.\n return getEncoding(\"cl100k_base\")\n }\n}\n\n/**\n * Construct an {@link OpenAiBackend} bound to a specific API key and model.\n */\nexport function createOpenAiBackend(config: OpenAiBackendConfig): OpenAiBackend {\n const model = config.model ?? DEFAULT_MODEL\n const tokenLimit = config.tokenLimit ?? DEFAULT_TOKEN_LIMIT\n const openai = new OpenAI({\n apiKey: config.apiKey,\n ...(config.baseURL ? { baseURL: config.baseURL } : {}),\n })\n const encoding = resolveEncoding(model)\n\n return {\n model,\n tokenLimit,\n\n countTokens(text: string): number {\n return encoding.encode(text).length\n },\n\n async chatCompletion(content: string): Promise<OpenAIOutput<string>> {\n const response = await openai.chat.completions.create({\n messages: [{ role: \"user\", content }],\n model,\n })\n return {\n output: response.choices[0]?.message.content ?? \"\",\n inputTokens: response.usage?.prompt_tokens ?? 0,\n outputTokens: response.usage?.completion_tokens ?? 0,\n }\n },\n\n async chatFunctionJson<T>(\n systemMessage: string,\n userMessage: string,\n schema: JSONSchema7Object,\n functionName: string,\n ): Promise<OpenAIOutput<T>> {\n try {\n const completion = await openai.chat.completions.create({\n model,\n messages: [\n { role: \"system\", content: systemMessage },\n { role: \"user\", content: userMessage },\n ],\n functions: [{ name: functionName, parameters: schema }],\n function_call: { name: functionName },\n })\n\n const functionCall = completion.choices[0]?.message.function_call\n if (!functionCall) {\n throw new AiResponseJsonError(\"No function call in OpenAI response\")\n }\n\n const inputTokens = completion.usage?.prompt_tokens ?? 0\n const outputTokens = completion.usage?.completion_tokens ?? 0\n\n try {\n const output = JSON.parse(functionCall.arguments) as T\n return { output, inputTokens, outputTokens }\n } catch (error) {\n if (error instanceof SyntaxError) {\n const msg = error.message.toLowerCase()\n if (\n msg.includes(\"unexpected end of json input\") ||\n msg.includes(\"unterminated string in js\")\n ) {\n throw new AiOutputLengthError(\n `Output tokens exceeded: ${error.message}`,\n { cause: error },\n )\n }\n throw new AiResponseJsonError(\n `Failed to parse JSON from OpenAI response: ${error.message}`,\n { cause: error },\n )\n }\n throw new AiResponseJsonError(\n \"Failed to parse JSON from OpenAI response (unknown reason)\",\n )\n }\n } catch (error) {\n if (error instanceof AiError) throw error\n if (error instanceof OpenAI.APIError) {\n const msg = error.message.toLowerCase()\n if (msg.includes(\"maximum context\")) {\n throw new AiInputLengthError(`Text too long: ${error.message}`, {\n cause: error,\n })\n }\n if (msg.includes(\"invalid schema\")) {\n throw new InvalidJsonSchemaError(`Invalid schema: ${error.message}`, {\n cause: error,\n })\n }\n throw new AiError(`OpenAI error: ${error.message}`, { cause: error })\n }\n throw new AiError(\"OpenAI error (unknown)\", {\n cause: error instanceof Error ? error : undefined,\n })\n }\n },\n }\n}\n","import {\n parseLocationsFromHtml,\n parseLocationsFromText,\n parseLocationsFromUrl,\n} from \"./locations\"\nimport { createOpenAiBackend, type OpenAiBackendConfig } from \"./openai\"\nimport type { GetLocationsResponse } from \"./types\"\n\n/**\n * A configured location-extraction pipeline. Hold onto one of these for the\n * lifetime of your app — it wraps an OpenAI client and a tiktoken encoder.\n */\nexport interface LocationParser {\n parseLocationsFromText(text: string): Promise<GetLocationsResponse>\n parseLocationsFromHtml(html: string): Promise<GetLocationsResponse>\n parseLocationsFromUrl(url: string): Promise<GetLocationsResponse>\n}\n\nexport type LocationParserConfig = OpenAiBackendConfig\n\n/**\n * Create a {@link LocationParser} bound to an OpenAI API key.\n *\n * @example\n * ```ts\n * const parser = createLocationParser({\n * apiKey: process.env.OPENAI_API_KEY!,\n * model: \"gpt-4o-mini\",\n * })\n * const { output } = await parser.parseLocationsFromUrl(\n * \"https://example.com/best-restaurants-tokyo\",\n * )\n * console.log(output.title, output.locations)\n * ```\n */\nexport function createLocationParser(config: LocationParserConfig): LocationParser {\n const backend = createOpenAiBackend(config)\n return {\n parseLocationsFromText: (text) => parseLocationsFromText(backend, text),\n parseLocationsFromHtml: (html) => parseLocationsFromHtml(backend, html),\n parseLocationsFromUrl: (url) => parseLocationsFromUrl(backend, url),\n }\n}\n"]}
|
|
1
|
+
{"version":3,"sources":["../../src/types/errors.ts","../../src/ai/errors.ts","../../src/ai/prompts.ts","../../src/scrape/errors.ts","../../src/scrape/html.ts","../../src/scrape/text.ts","../../src/ai/summarize.ts","../../src/ai/locations.ts","../../src/ai/openai.ts","../../src/ai/anthropic.ts","../../src/ai/parser.ts"],"names":["resolveEncoding","encodingForModel","getEncoding"],"mappings":"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","file":"index.js","sourcesContent":["/**\n * Base error class for every error thrown by the mapthis package.\n *\n * All feature-specific error classes (scrape, search, ai, geocoding) extend\n * this, so consumers can do `catch (e) { if (e instanceof MapthisError) ... }`\n * to distinguish library errors from other exceptions.\n */\nexport class MapthisError extends Error {\n constructor(message?: string, options?: ErrorOptions) {\n super(message, options)\n this.name = \"MapthisError\"\n }\n}\n","import { MapthisError } from \"../types/errors\"\n\nexport class AiError extends MapthisError {\n constructor(message?: string, options?: ErrorOptions) {\n super(message, options)\n this.name = \"AiError\"\n }\n}\n\n/**\n * The LLM returned zero locations after deduplication.\n */\nexport class NoLocationsFoundError extends AiError {\n constructor(message?: string, options?: ErrorOptions) {\n super(message ?? \"No locations found in input\", options)\n this.name = \"NoLocationsFoundError\"\n }\n}\n\n/**\n * Summarization pipeline failed (one of the chunk calls rejected).\n */\nexport class SummarizeTextError extends AiError {\n constructor(message?: string, options?: ErrorOptions) {\n super(message, options)\n this.name = \"SummarizeTextError\"\n }\n}\n\n/**\n * A JSON schema passed to the LLM was rejected as invalid by the provider.\n */\nexport class InvalidJsonSchemaError extends AiError {\n constructor(message?: string, options?: ErrorOptions) {\n super(message, options)\n this.name = \"InvalidJsonSchemaError\"\n }\n}\n\n/**\n * The LLM response could not be parsed as JSON (typically because the output\n * was truncated or malformed).\n */\nexport class AiResponseJsonError extends AiError {\n constructor(message?: string, options?: ErrorOptions) {\n super(message, options)\n this.name = \"AiResponseJsonError\"\n }\n}\n\n/**\n * Input text exceeded the model's context window.\n */\nexport class AiInputLengthError extends AiError {\n constructor(message?: string, options?: ErrorOptions) {\n super(message, options)\n this.name = \"AiInputLengthError\"\n }\n}\n\n/**\n * The LLM hit the output token limit before completing its JSON response.\n */\nexport class AiOutputLengthError extends AiError {\n constructor(message?: string, options?: ErrorOptions) {\n super(message, options)\n this.name = \"AiOutputLengthError\"\n }\n}\n","import type { JSONSchema7Object } from \"json-schema\"\n\n/**\n * Semantic version of the location-extraction system prompt. Stored alongside\n * each place record in the private app so results can be re-run when the\n * prompt is updated. Bump when you change {@link LOCATIONS_SYSTEM_MESSAGE} or\n * {@link LOCATIONS_SCHEMA} in a way that meaningfully changes output.\n */\nexport const LOCATIONS_PROMPT_VERSION = \"0.0.1\"\n\nexport const LOCATIONS_FUNCTIONS_NAME = \"get_places\"\n\nexport const LOCATIONS_SYSTEM_MESSAGE = `You parse a list of place addresses, corresponding brief one-sentence descriptions, and a descriptive title (preferably directly quoted) from text. These addresses might be countries, cities, attractions, parks, bars, etc.\nIf the text seems to be listing places that are all within a single parent place, you don't return the parent place, but you do add it to all the childrens' addresses.\nFor example, for an article about places in greece, you would not return \"greece\", but if the Acropolis was mentioned, you would return \"Acropolis, Athens, Greece\"\n\nYou ignore extraneous text, such as recommended articles, advertisments, etc.\n`\n\nexport const LOCATIONS_SCHEMA: JSONSchema7Object = {\n type: \"object\",\n required: [\"locations\", \"title\"],\n properties: {\n locations: {\n type: \"array\",\n items: {\n type: \"object\",\n required: [\"address\", \"description\"],\n properties: {\n address: { type: \"string\" },\n description: { type: \"string\" },\n },\n },\n },\n title: { type: \"string\" },\n },\n}\n\nexport const LOCATIONS_MAX_OUTPUT_TOKENS = 500\n\n/**\n * Semantic version of the summarization prompt. Bump when meaningful changes\n * are made to {@link SUMMARIZATION_PROMPT}.\n */\nexport const SUMMARIZE_PROMPT_VERSION = \"0.0.1\"\n\nexport const SUMMARIZATION_PROMPT = `Summarize the given text. The summary must reduce the number of characters from the original text by at least a factor of {RATIO}.\nFor example, text of 1000 characters must output a summary less than {EXAMPLE} characters.\nThis summary will be used to extract a list of locations and addresses, so keep any text that seems to be a list of locations/places, a location and paragraph describing it, or otherwise clearly a location name or address.\nHere is the text:\n`\n","import { MapthisError } from \"../types/errors\"\n\nexport class ScrapeError extends MapthisError {\n constructor(message?: string, options?: ErrorOptions) {\n super(message, options)\n this.name = \"ScrapeError\"\n }\n}\n\n/**\n * Thrown when a provided URL fails validation or cannot be parsed by `fetch`.\n */\nexport class InvalidUrlError extends ScrapeError {\n constructor(message?: string, options?: ErrorOptions) {\n super(message, options)\n this.name = \"InvalidUrlError\"\n }\n}\n\n/**\n * Thrown when a remote server returns 401 or 403 for an HTML fetch, meaning\n * the page does not permit anonymous scraping.\n */\nexport class HtmlUnauthorizedError extends ScrapeError {\n constructor(message?: string, options?: ErrorOptions) {\n super(message, options)\n this.name = \"HtmlUnauthorizedError\"\n }\n}\n\n/**\n * Thrown when the underlying `html-to-text` conversion fails.\n */\nexport class HtmlToTextError extends ScrapeError {\n constructor(message?: string, options?: ErrorOptions) {\n super(message, options)\n this.name = \"HtmlToTextError\"\n }\n}\n","import { z } from \"zod\"\nimport { HtmlUnauthorizedError, InvalidUrlError, ScrapeError } from \"./errors\"\n\nconst withHttps = (url: string): string => {\n const hasHttp = /^http?:\\/\\//i.test(url)\n const hasHttps = /^https?:\\/\\//i.test(url)\n if (!hasHttp && !hasHttps) return `https://${url}`\n return url\n}\n\n/**\n * Fetch the raw HTML of a URL, performing light validation first.\n *\n * - URLs without a scheme are prefixed with `https://`.\n * - URL shape is validated via Zod before the network call.\n * - 401/403 responses throw {@link HtmlUnauthorizedError}.\n * - Other non-OK responses throw {@link ScrapeError} with the status text.\n * - `TypeError` from `fetch` (typically malformed URL) is rethrown as\n * {@link InvalidUrlError}.\n *\n * This function does not set any special headers, user agent, or cookies.\n * Sites with aggressive bot-detection may return empty bodies or errors; the\n * caller is responsible for retry / user-agent rotation strategies if needed.\n */\nexport async function getHtmlFromUrl(url: string): Promise<string> {\n const urlFixed = withHttps(url.toLowerCase())\n const isUrl = z.string().url().safeParse(urlFixed)\n if (!isUrl.success) throw new InvalidUrlError(`Invalid URL: ${url}`)\n\n try {\n const response = await fetch(urlFixed)\n const html = await response.text()\n if (response.status === 403 || response.status === 401) {\n throw new HtmlUnauthorizedError(\n `${response.status} Not authorized to scrape this website`,\n )\n }\n if (!response.ok) {\n throw new ScrapeError(\n `Bad response when getting HTML. Status: ${response.status} ${response.statusText}`,\n )\n }\n return html\n } catch (error) {\n if (error instanceof TypeError) {\n throw new InvalidUrlError(`Invalid URL. Original error: ${error.message}`)\n }\n throw error\n }\n}\n","import { convert } from \"html-to-text\"\nimport { HtmlToTextError } from \"./errors\"\nimport { getHtmlFromUrl } from \"./html\"\n\nconst SKIP_SELECTORS = [\n \"img\",\n \"header\",\n \"footer\",\n \"audio\",\n \"button\",\n \"canvas\",\n \"code\",\n \"nav\",\n \"#nav\",\n \"figure\",\n \"figcaption\",\n \".comment\",\n \".comments\",\n \"#comments\",\n \"#related-posts\",\n \"#related\",\n \".related\",\n \".related-posts\",\n] as const\n\n/**\n * Convert raw HTML to plain text suitable for LLM consumption.\n *\n * The converter is configured to:\n * - strip anchor hrefs when they duplicate the link text;\n * - skip navigation, footer, comments, related-post widgets, images, and\n * other non-content elements;\n * - collapse repeated blank lines and double spaces so token count doesn't\n * balloon from whitespace.\n */\nexport function htmlToText(html: string): string {\n try {\n let out = convert(html, {\n wordwrap: false,\n selectors: [\n {\n selector: \"a\",\n options: {\n ignoreHref: true,\n hideLinkHrefIfSameAsText: true,\n },\n },\n ...SKIP_SELECTORS.map((tag) => ({ selector: tag, format: \"skip\" })),\n ],\n })\n out = out.replaceAll(\"\\r\\n\", \"\\n\")\n // Iteratively collapse blank lines and double spaces. 20 passes is\n // overkill for any realistic input but cheap and bounds worst case.\n for (let i = 0; i < 20; i++) {\n out = out.replaceAll(\"\\n\\n\\n\", \"\\n\\n\")\n out = out.replaceAll(\" \", \" \")\n }\n return out\n } catch (error) {\n if (error instanceof Error) {\n throw new HtmlToTextError(`HTML to text conversion failed: ${error.message}`, {\n cause: error,\n })\n }\n throw new HtmlToTextError(\"HTML to text conversion failed\")\n }\n}\n\n/**\n * Convenience: fetch a URL and convert its HTML to plain text in one call.\n *\n * Equivalent to `htmlToText(await getHtmlFromUrl(url))`.\n */\nexport async function getTextFromUrl(url: string): Promise<string> {\n const html = await getHtmlFromUrl(url)\n return htmlToText(html)\n}\n","import type { LlmBackend } from \"./backend\"\nimport { SummarizeTextError } from \"./errors\"\nimport { SUMMARIZATION_PROMPT, SUMMARIZE_PROMPT_VERSION } from \"./prompts\"\nimport type { SummarizedText } from \"./types\"\n\n/**\n * Reduce a large text to a summary that will fit inside the model's context\n * window alongside a known downstream prompt (e.g. the location-extraction\n * call).\n *\n * Algorithm overview\n * ------------------\n * We need to fit both the summary and a downstream \"final\" prompt into the\n * model's context window. Given:\n *\n * - `token_limit` — model context window (with a safety factor)\n * - `text_tokens` — size of the input text\n * - `summarize_prompt_tokens` — size of the summarization prompt itself\n * - `final_prompt_tokens` — size of the downstream prompt\n * - `final_output_tokens` — expected size of the downstream response\n *\n * We split the input into chunks, summarize each in parallel, and concatenate\n * the results. The per-chunk size is chosen so that the combined summary\n * leaves room for the downstream prompt:\n *\n * final_input_tokens = token_limit - final_output_tokens\n * summary_output_tokens = final_input_tokens - final_prompt_tokens\n * num_chunks = ceil((text_tokens + summary_output_tokens)\n * / (token_limit - summarize_prompt_tokens))\n * chunk_output_tokens = summary_output_tokens / num_chunks\n * chunk_input_tokens = token_limit - summarize_prompt_tokens - chunk_output_tokens\n *\n * A safety factor is applied to chunk output sizes because LLMs routinely\n * overshoot explicit length instructions.\n */\nexport async function summarizeText(\n backend: LlmBackend,\n text: string,\n finalPrompt = \"\",\n finalOutputTokens = 0,\n chunkOutputSafetyFactor = 0.3,\n tokenLimitSafetyFactor = 0.02,\n): Promise<SummarizedText> {\n const tokenLimit = backend.tokenLimit * (1 - tokenLimitSafetyFactor)\n const textTokens = backend.countTokens(text)\n const summarizePromptTokens = backend.countTokens(SUMMARIZATION_PROMPT)\n const finalPromptTokens = backend.countTokens(finalPrompt)\n\n const finalInputTokens = tokenLimit - finalOutputTokens\n const summaryOutputTokens = finalInputTokens - finalPromptTokens\n\n const numChunks = Math.max(\n 1,\n Math.ceil((textTokens + summaryOutputTokens) / (tokenLimit - summarizePromptTokens)),\n )\n const chunkOutputTokens = Math.floor(\n (summaryOutputTokens / numChunks) * (1 - chunkOutputSafetyFactor),\n )\n const chunkInputTokens = Math.floor(tokenLimit - summarizePromptTokens - chunkOutputTokens)\n const summaryRatio = chunkOutputTokens / chunkInputTokens\n\n // Split the text into chunks that each fit inside `chunkInputTokens`.\n // Lines longer than a single chunk are force-split by token, not by\n // character, so UTF-8 multibyte characters aren't corrupted.\n const textLines = text.split(\"\\n\")\n const chunks: string[] = []\n\n let currentChunk = \"\"\n let currentChunkTokens = 0\n const nextChunk = (): void => {\n if (currentChunk) chunks.push(currentChunk)\n currentChunk = \"\"\n currentChunkTokens = 0\n }\n\n // We need an encoder for the force-split path. The backend's countTokens\n // gives us length but not the encoded array; for force-splitting we only\n // need to chunk by an approximate token count using the same rounding.\n // To keep this function backend-agnostic, we fall back to character-based\n // splitting for over-long lines (rare edge case in practice).\n for (const line of textLines) {\n const numLineTokens = backend.countTokens(line) + 1 // +1 for the newline\n\n if (numLineTokens > chunkInputTokens) {\n // Long line: flush current chunk, then split this line by\n // approximate token ratio into sub-chunks.\n nextChunk()\n const charsPerToken = line.length / Math.max(1, backend.countTokens(line))\n const charsPerChunk = Math.max(1, Math.floor(chunkInputTokens * charsPerToken))\n for (let i = 0; i < line.length; i += charsPerChunk) {\n chunks.push(line.slice(i, i + charsPerChunk))\n }\n continue\n }\n\n if (currentChunkTokens + numLineTokens > chunkInputTokens) {\n nextChunk()\n }\n currentChunkTokens += numLineTokens\n currentChunk += line + \"\\n\"\n }\n nextChunk()\n\n try {\n const ratioText = (Math.ceil(summaryRatio * 10) / 10).toFixed(1)\n const exampleText = Math.ceil(1000 / Math.max(summaryRatio, 0.01)).toFixed(0)\n const summaryPrompt = SUMMARIZATION_PROMPT.replace(\"{RATIO}\", ratioText).replace(\n \"{EXAMPLE}\",\n exampleText,\n )\n\n let inputTokens = 0\n let outputTokens = 0\n const summaries = await Promise.all(\n chunks.map(async (chunk) => {\n const response = await backend.chatCompletion(summaryPrompt + chunk)\n inputTokens += response.inputTokens\n outputTokens += response.outputTokens\n return response.output\n }),\n )\n\n const summarized = summaries.join(\"\\n\")\n const summarizedTokens = backend.countTokens(summarized)\n\n return {\n text,\n textTokens,\n summarized,\n summarizedTokens,\n inputTokens,\n outputTokens,\n chunks: chunks.length,\n summarizePromptVersion: SUMMARIZE_PROMPT_VERSION,\n }\n } catch (error) {\n if (error instanceof Error) {\n throw new SummarizeTextError(`Failed to summarize text: ${error.message}`, {\n cause: error,\n })\n }\n throw new SummarizeTextError(\"Failed to summarize text (unknown error)\")\n }\n}\n","import { getHtmlFromUrl } from \"../scrape/html\"\nimport { htmlToText } from \"../scrape/text\"\nimport type { ParsedLocations } from \"../types/domain\"\nimport type { LlmBackend } from \"./backend\"\nimport { NoLocationsFoundError } from \"./errors\"\nimport {\n LOCATIONS_FUNCTIONS_NAME,\n LOCATIONS_MAX_OUTPUT_TOKENS,\n LOCATIONS_PROMPT_VERSION,\n LOCATIONS_SCHEMA,\n LOCATIONS_SYSTEM_MESSAGE,\n} from \"./prompts\"\nimport { summarizeText } from \"./summarize\"\nimport type { GetLocationsResponse } from \"./types\"\n\nconst FINAL_PROMPT = LOCATIONS_SYSTEM_MESSAGE + JSON.stringify(LOCATIONS_SCHEMA) + LOCATIONS_FUNCTIONS_NAME\n\nasync function callLocationExtraction(backend: LlmBackend, text: string) {\n const out = await backend.chatFunctionJson<ParsedLocations>(\n LOCATIONS_SYSTEM_MESSAGE,\n text,\n LOCATIONS_SCHEMA,\n LOCATIONS_FUNCTIONS_NAME,\n )\n\n // LLM frequently returns the same address twice across different prompts.\n // Dedupe by address string.\n const seen = new Set<string>()\n const unique = out.output.locations.filter((loc) => {\n if (seen.has(loc.address)) return false\n seen.add(loc.address)\n return true\n })\n if (unique.length === 0) throw new NoLocationsFoundError()\n out.output.locations = unique\n\n return out\n}\n\n/**\n * Parse locations from freeform text. Two-stage: summarize to fit the context\n * window, then a function-calling JSON completion for extraction.\n */\nexport async function parseLocationsFromText(\n backend: LlmBackend,\n text: string,\n): Promise<GetLocationsResponse> {\n const {\n summarized,\n inputTokens: sumIn,\n outputTokens: sumOut,\n textTokens,\n summarizedTokens,\n summarizePromptVersion,\n } = await summarizeText(backend, text, FINAL_PROMPT, LOCATIONS_MAX_OUTPUT_TOKENS)\n\n const {\n output,\n inputTokens: locIn,\n outputTokens: locOut,\n } = await callLocationExtraction(backend, summarized)\n\n return {\n output,\n locationInputTokens: locIn,\n locationOutputTokens: locOut,\n summaryInputTokens: sumIn,\n summaryOutputTokens: sumOut,\n inputTokens: sumIn + locIn,\n outputTokens: sumOut + locOut,\n textTokens,\n summarizedTokens,\n summaryPromptVersion: summarizePromptVersion,\n locationPromptVersion: LOCATIONS_PROMPT_VERSION,\n }\n}\n\n/**\n * Parse locations from HTML. Equivalent to `htmlToText` followed by\n * {@link parseLocationsFromText}.\n */\nexport async function parseLocationsFromHtml(\n backend: LlmBackend,\n html: string,\n): Promise<GetLocationsResponse> {\n return parseLocationsFromText(backend, htmlToText(html))\n}\n\n/**\n * Parse locations from a URL. Equivalent to fetching the HTML, converting to\n * text, and calling {@link parseLocationsFromText}. Throws any scrape errors\n * unchanged.\n */\nexport async function parseLocationsFromUrl(\n backend: LlmBackend,\n url: string,\n): Promise<GetLocationsResponse> {\n const html = await getHtmlFromUrl(url)\n return parseLocationsFromHtml(backend, html)\n}\n","import { encodingForModel, getEncoding, type Tiktoken, type TiktokenModel } from \"js-tiktoken\"\nimport OpenAI from \"openai\"\nimport type { JSONSchema7Object } from \"json-schema\"\nimport {\n AiInputLengthError,\n AiOutputLengthError,\n AiResponseJsonError,\n AiError,\n InvalidJsonSchemaError,\n} from \"./errors\"\nimport type { LlmBackend } from \"./backend\"\nimport type { OpenAIOutput } from \"./types\"\n\nexport const DEFAULT_MODEL = \"gpt-4o-mini\"\nexport const DEFAULT_TOKEN_LIMIT = 128000\n\n/**\n * @deprecated Use {@link LlmBackend} instead. Kept as an alias so existing\n * consumers that import `OpenAiBackend` keep compiling.\n */\nexport type OpenAiBackend = LlmBackend\n\nexport interface OpenAiBackendConfig {\n /** OpenAI API key. Passed directly to the SDK. */\n apiKey: string\n /** Model name passed to the completions API. Defaults to `gpt-4o-mini`. */\n model?: string\n /** Context-window budget used by the summarizer. Defaults to 128k. */\n tokenLimit?: number\n /**\n * Optional base URL for the OpenAI API. Use this to route through an\n * OpenAI-compatible gateway (Azure, Together, etc.).\n */\n baseURL?: string\n}\n\nfunction resolveEncoding(model: string): Tiktoken {\n try {\n return encodingForModel(model as TiktokenModel)\n } catch {\n // Fallback for models tiktoken does not recognize by name.\n return getEncoding(\"cl100k_base\")\n }\n}\n\n/**\n * Construct an {@link LlmBackend} bound to a specific OpenAI API key and model.\n */\nexport function createOpenAiBackend(config: OpenAiBackendConfig): LlmBackend {\n const model = config.model ?? DEFAULT_MODEL\n const tokenLimit = config.tokenLimit ?? DEFAULT_TOKEN_LIMIT\n const openai = new OpenAI({\n apiKey: config.apiKey,\n ...(config.baseURL ? { baseURL: config.baseURL } : {}),\n })\n const encoding = resolveEncoding(model)\n\n return {\n model,\n tokenLimit,\n\n countTokens(text: string): number {\n return encoding.encode(text).length\n },\n\n async chatCompletion(content: string): Promise<OpenAIOutput<string>> {\n const response = await openai.chat.completions.create({\n messages: [{ role: \"user\", content }],\n model,\n })\n return {\n output: response.choices[0]?.message.content ?? \"\",\n inputTokens: response.usage?.prompt_tokens ?? 0,\n outputTokens: response.usage?.completion_tokens ?? 0,\n }\n },\n\n async chatFunctionJson<T>(\n systemMessage: string,\n userMessage: string,\n schema: JSONSchema7Object,\n functionName: string,\n ): Promise<OpenAIOutput<T>> {\n try {\n const completion = await openai.chat.completions.create({\n model,\n messages: [\n { role: \"system\", content: systemMessage },\n { role: \"user\", content: userMessage },\n ],\n functions: [{ name: functionName, parameters: schema }],\n function_call: { name: functionName },\n })\n\n const functionCall = completion.choices[0]?.message.function_call\n if (!functionCall) {\n throw new AiResponseJsonError(\"No function call in OpenAI response\")\n }\n\n const inputTokens = completion.usage?.prompt_tokens ?? 0\n const outputTokens = completion.usage?.completion_tokens ?? 0\n\n try {\n const output = JSON.parse(functionCall.arguments) as T\n return { output, inputTokens, outputTokens }\n } catch (error) {\n if (error instanceof SyntaxError) {\n const msg = error.message.toLowerCase()\n if (\n msg.includes(\"unexpected end of json input\") ||\n msg.includes(\"unterminated string in js\")\n ) {\n throw new AiOutputLengthError(\n `Output tokens exceeded: ${error.message}`,\n { cause: error },\n )\n }\n throw new AiResponseJsonError(\n `Failed to parse JSON from OpenAI response: ${error.message}`,\n { cause: error },\n )\n }\n throw new AiResponseJsonError(\n \"Failed to parse JSON from OpenAI response (unknown reason)\",\n )\n }\n } catch (error) {\n if (error instanceof AiError) throw error\n if (error instanceof OpenAI.APIError) {\n const msg = error.message.toLowerCase()\n if (msg.includes(\"maximum context\")) {\n throw new AiInputLengthError(`Text too long: ${error.message}`, {\n cause: error,\n })\n }\n if (msg.includes(\"invalid schema\")) {\n throw new InvalidJsonSchemaError(`Invalid schema: ${error.message}`, {\n cause: error,\n })\n }\n throw new AiError(`OpenAI error: ${error.message}`, { cause: error })\n }\n throw new AiError(\"OpenAI error (unknown)\", {\n cause: error instanceof Error ? error : undefined,\n })\n }\n },\n }\n}\n","import Anthropic from \"@anthropic-ai/sdk\"\nimport { encodingForModel, getEncoding, type Tiktoken, type TiktokenModel } from \"js-tiktoken\"\nimport type { JSONSchema7Object } from \"json-schema\"\nimport {\n AiError,\n AiInputLengthError,\n AiOutputLengthError,\n AiResponseJsonError,\n InvalidJsonSchemaError,\n} from \"./errors\"\nimport type { LlmBackend } from \"./backend\"\nimport type { OpenAIOutput } from \"./types\"\n\nexport const DEFAULT_ANTHROPIC_MODEL = \"claude-haiku-4-5-20251001\"\nexport const DEFAULT_ANTHROPIC_TOKEN_LIMIT = 200_000\nexport const DEFAULT_ANTHROPIC_MAX_OUTPUT_TOKENS = 4096\n\nexport interface AnthropicBackendConfig {\n /** Anthropic API key. Passed directly to the SDK. */\n apiKey: string\n /** Model name. Defaults to {@link DEFAULT_ANTHROPIC_MODEL}. */\n model?: string\n /** Context-window budget used by the summarizer. Defaults to 200k. */\n tokenLimit?: number\n /** Optional override for max_tokens on each request. Defaults to 4096. */\n maxOutputTokens?: number\n /**\n * Optional base URL for an Anthropic-compatible gateway (Bedrock, Vertex\n * proxy, etc.).\n */\n baseURL?: string\n}\n\n/**\n * Token counting fallback. The Anthropic SDK exposes a remote\n * `messages.countTokens` endpoint, but the summarizer calls `countTokens`\n * thousands of times during chunking — making it a network hop would be\n * prohibitive. We approximate with cl100k_base, which matches GPT-4-class\n * tokenizers closely enough for chunk sizing (which already has a 30%\n * safety factor applied).\n */\nfunction resolveEncoding(model: string): Tiktoken {\n try {\n return encodingForModel(model as TiktokenModel)\n } catch {\n return getEncoding(\"cl100k_base\")\n }\n}\n\n/**\n * Construct an {@link LlmBackend} backed by Anthropic's Messages API. Use this\n * when wiring up {@link createLocationParser} or as a drop-in replacement for\n * {@link createOpenAiBackend} in any consumer that holds the interface.\n */\nexport function createAnthropicBackend(config: AnthropicBackendConfig): LlmBackend {\n const model = config.model ?? DEFAULT_ANTHROPIC_MODEL\n const tokenLimit = config.tokenLimit ?? DEFAULT_ANTHROPIC_TOKEN_LIMIT\n const maxOutputTokens = config.maxOutputTokens ?? DEFAULT_ANTHROPIC_MAX_OUTPUT_TOKENS\n const client = new Anthropic({\n apiKey: config.apiKey,\n ...(config.baseURL ? { baseURL: config.baseURL } : {}),\n })\n const encoding = resolveEncoding(model)\n\n return {\n model,\n tokenLimit,\n\n countTokens(text: string): number {\n return encoding.encode(text).length\n },\n\n async chatCompletion(content: string): Promise<OpenAIOutput<string>> {\n const response = await client.messages.create({\n model,\n max_tokens: maxOutputTokens,\n messages: [{ role: \"user\", content }],\n })\n const text = response.content\n .filter((b): b is Anthropic.TextBlock => b.type === \"text\")\n .map((b) => b.text)\n .join(\"\")\n return {\n output: text,\n inputTokens: response.usage.input_tokens,\n outputTokens: response.usage.output_tokens,\n }\n },\n\n async chatFunctionJson<T>(\n systemMessage: string,\n userMessage: string,\n schema: JSONSchema7Object,\n functionName: string,\n ): Promise<OpenAIOutput<T>> {\n try {\n const response = await client.messages.create({\n model,\n max_tokens: maxOutputTokens,\n system: systemMessage,\n tools: [\n {\n name: functionName,\n description: `Return structured output matching the ${functionName} schema.`,\n input_schema: schema as Anthropic.Tool.InputSchema,\n },\n ],\n tool_choice: { type: \"tool\", name: functionName },\n messages: [{ role: \"user\", content: userMessage }],\n })\n\n const toolUse = response.content.find(\n (b): b is Anthropic.ToolUseBlock => b.type === \"tool_use\",\n )\n if (!toolUse) {\n throw new AiResponseJsonError(\"No tool use in Anthropic response\")\n }\n if (response.stop_reason === \"max_tokens\") {\n throw new AiOutputLengthError(\n `Anthropic hit max_tokens (${maxOutputTokens}) before completing tool call`,\n )\n }\n\n return {\n output: toolUse.input as T,\n inputTokens: response.usage.input_tokens,\n outputTokens: response.usage.output_tokens,\n }\n } catch (error) {\n if (error instanceof AiError) throw error\n if (error instanceof Anthropic.APIError) {\n const msg = (error.message ?? \"\").toLowerCase()\n if (msg.includes(\"prompt is too long\") || msg.includes(\"max_tokens\")) {\n throw new AiInputLengthError(`Text too long: ${error.message}`, {\n cause: error,\n })\n }\n if (msg.includes(\"invalid\") && msg.includes(\"schema\")) {\n throw new InvalidJsonSchemaError(`Invalid schema: ${error.message}`, {\n cause: error,\n })\n }\n throw new AiError(`Anthropic error: ${error.message}`, { cause: error })\n }\n throw new AiError(\"Anthropic error (unknown)\", {\n cause: error instanceof Error ? error : undefined,\n })\n }\n },\n }\n}\n","import type { LlmBackend } from \"./backend\"\nimport {\n parseLocationsFromHtml,\n parseLocationsFromText,\n parseLocationsFromUrl,\n} from \"./locations\"\nimport { createOpenAiBackend, type OpenAiBackendConfig } from \"./openai\"\nimport { createAnthropicBackend, type AnthropicBackendConfig } from \"./anthropic\"\nimport type { GetLocationsResponse } from \"./types\"\n\n/**\n * A configured location-extraction pipeline. Hold onto one of these for the\n * lifetime of your app — it wraps an LLM client and a tiktoken encoder.\n */\nexport interface LocationParser {\n parseLocationsFromText(text: string): Promise<GetLocationsResponse>\n parseLocationsFromHtml(html: string): Promise<GetLocationsResponse>\n parseLocationsFromUrl(url: string): Promise<GetLocationsResponse>\n}\n\n/**\n * Configuration for {@link createLocationParser}.\n *\n * - `{ provider: \"openai\", apiKey, ... }` — uses OpenAI's chat completions API\n * with function-calling for structured output (default if omitted).\n * - `{ provider: \"anthropic\", apiKey, ... }` — uses Anthropic's Messages API\n * with tool-use for structured output.\n * - `{ backend }` — pass any object satisfying {@link LlmBackend} directly,\n * useful for testing or for adapters that aren't bundled here.\n *\n * For backwards compatibility a bare {@link OpenAiBackendConfig} (no\n * `provider` field) is treated as `{ provider: \"openai\", ... }`.\n */\nexport type LocationParserConfig =\n | ({ provider?: \"openai\" } & OpenAiBackendConfig)\n | ({ provider: \"anthropic\" } & AnthropicBackendConfig)\n | { backend: LlmBackend }\n\nfunction resolveBackend(config: LocationParserConfig): LlmBackend {\n if (\"backend\" in config) return config.backend\n if (config.provider === \"anthropic\") return createAnthropicBackend(config)\n return createOpenAiBackend(config)\n}\n\n/**\n * Create a {@link LocationParser} bound to an LLM backend.\n *\n * @example OpenAI (default)\n * ```ts\n * const parser = createLocationParser({\n * apiKey: process.env.OPENAI_API_KEY!,\n * model: \"gpt-4o-mini\",\n * })\n * ```\n *\n * @example Anthropic\n * ```ts\n * const parser = createLocationParser({\n * provider: \"anthropic\",\n * apiKey: process.env.ANTHROPIC_API_KEY!,\n * model: \"claude-haiku-4-5-20251001\",\n * })\n * ```\n *\n * @example Bring-your-own backend\n * ```ts\n * const parser = createLocationParser({ backend: myCustomBackend })\n * ```\n */\nexport function createLocationParser(config: LocationParserConfig): LocationParser {\n const backend = resolveBackend(config)\n return {\n parseLocationsFromText: (text) => parseLocationsFromText(backend, text),\n parseLocationsFromHtml: (html) => parseLocationsFromHtml(backend, html),\n parseLocationsFromUrl: (url) => parseLocationsFromUrl(backend, url),\n }\n}\n"]}
|
package/dist/generate/index.cjs
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
var zod = require('zod');
|
|
4
4
|
|
|
5
5
|
// src/types/schemas.ts
|
|
6
|
-
zod.z.literal("openai");
|
|
6
|
+
zod.z.union([zod.z.literal("openai"), zod.z.literal("anthropic")]);
|
|
7
7
|
zod.z.union([zod.z.literal("google"), zod.z.literal("locationiq")]);
|
|
8
8
|
var generationSourceType = zod.z.union([
|
|
9
9
|
zod.z.literal("blank"),
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/types/schemas.ts","../../src/generate/generator.ts"],"names":["z"],"mappings":"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","file":"index.cjs","sourcesContent":["import { z } from \"zod\"\n\n/**\n * Runtime validation schemas and their inferred types.\n *\n * These schemas define the public contract between producers and consumers of\n * mapthis data. They are intentionally framework-agnostic — no Prisma, no\n * Next.js, no auth assumptions.\n */\n\n// --- Providers & source types ------------------------------------------------\n\nexport const llmProvider = z.literal(\"openai\") // TODO: add other LLM support\nexport type LlmProvider = z.infer<typeof llmProvider>\n\nexport const placeProvider = z.union([z.literal(\"google\"), z.literal(\"locationiq\")])\nexport type PlaceProvider = z.infer<typeof placeProvider>\n\nexport const generationSourceType = z.union([\n z.literal(\"blank\"),\n z.literal(\"autocomplete\"),\n z.literal(\"url\"),\n z.literal(\"text\"),\n z.literal(\"list\"),\n])\nexport type GenerationSourceType = z.infer<typeof generationSourceType>\n\n// --- Geographic primitives ---------------------------------------------------\n\nexport const latitude = z.coerce.number().min(-90, \"Min latitude is -90\").max(90, \"Max latitude is 90\")\nexport const longitude = z.coerce.number().min(-180, \"Min longitude is -180\").max(180, \"Max longitude is 180\")\n\nexport const coordinates = z.object({\n lat: latitude,\n lng: longitude,\n})\nexport type Coordinates = z.infer<typeof coordinates>\n\n// --- Input schemas (freeform sources) ----------------------------------------\n\nexport const placesFromUrl = z.string().url({ message: \"Invalid url\" }).min(6).max(2048)\n\nexport const placesFromList = z\n .preprocess(\n (v) =>\n String(v)\n .split(\"\\n\")\n .map((line) => line.trim())\n .filter((line) => !!line)\n .join(\"\\n\"),\n z.string().min(6, \"Must be at least 6 characters\").max(1000, \"List too long\"),\n )\n .refine(\n (v) => !v.split(\"\\n\").find((line) => line.length < 2),\n \"All lines must be at least 2 characters\",\n )\n\nexport const placesFromText = z\n .string()\n .min(6, \"Must be at least 6 characters\")\n .max(2000, \"Over character limit\")\n\n// --- Map field schemas -------------------------------------------------------\n\nexport const mapExternalId = z.string().min(6).max(100)\nexport const mapTitle = z\n .string()\n .min(3, { message: \"Title must be at least 3 characters\" })\n .max(100, { message: \"Title must be at most 100 characters\" })\nexport const mapDescription = z.string().max(1000, { message: \"Limit is 1000 characters\" })\nexport const mapCreationQuery = z\n .string()\n .min(10, \"Minimum 10 characters for search\")\n .max(200, \"Maximum 200 characters\")\n\n// --- Group field schemas -----------------------------------------------------\n\nexport const groupName = z\n .string()\n .min(3, { message: \"Name must be at least 3 characters\" })\n .max(200, { message: \"Name must be at most 200 characters\" })\nexport const groupDescription = z.string().max(400, { message: \"Limit is 400 characters\" })\nexport const groupColor = z.string().startsWith(\"#\").length(7)\n\n// --- Place field schemas -----------------------------------------------------\n\nexport const placeName = z\n .string()\n .min(2, { message: \"Name must be at least 2 characters\" })\n .max(200, { message: \"Name must be at most 200 characters\" })\nexport const placeDescription = z.string().max(400, { message: \"Limit is 400 characters\" })\n\n// --- Map creation composites -------------------------------------------------\n\nexport const createMapBase = z.object({\n title: mapTitle.optional(),\n description: mapDescription.optional(),\n externalId: mapExternalId.optional(),\n})\n\nexport const createMapSchema = createMapBase.extend({\n sourceType: generationSourceType,\n source: z.string().optional(),\n})\n\nexport const createBlankMapSchema = createMapBase\n\nexport const createMapFromUrlSchema = createMapBase.extend({\n url: placesFromUrl,\n})\nexport const createMapFromListSchema = createMapBase.extend({\n list: placesFromList,\n})\nexport const createMapFromTextSchema = createMapBase.extend({\n text: placesFromText,\n})\nexport const createMapFromQueryOrUrlSchema = createMapBase.extend({\n queryOrUrl: z.union([mapCreationQuery, placesFromUrl]),\n})\n\n// --- Adding places -----------------------------------------------------------\n\nexport const addPlacesBaseSchema = z.object({\n sourceType: generationSourceType,\n source: z.string(),\n})\n\nexport const addPlacesFromSourceSchema = addPlacesBaseSchema.extend({\n mapId: z.string(),\n groupId: z.string().optional(),\n})\n\nexport const addPlacesFromUrlSchema = z.object({\n url: placesFromUrl,\n})\nexport const addPlacesFromListSchema = z.object({\n list: placesFromList,\n})\nexport const addPlacesFromTextSchema = z.object({\n text: placesFromText,\n})\n\nexport const providerAutocompletePlace = z.object({\n query: z.string(),\n provider: z.literal(\"google\"),\n providerId: z.string(),\n name: z.string(),\n description: z.string().optional(),\n address: z.string(),\n lat: z.number().min(-90).max(90),\n lng: z.number().min(-180).max(180),\n})\nexport type ProviderAutocompletePlace = z.infer<typeof providerAutocompletePlace>\n\nexport const addPlacesFromClientAutocompleteSchema = providerAutocompletePlace.extend({\n mapId: z.string(),\n groupId: z.string().optional(),\n})\n\n// --- Updates -----------------------------------------------------------------\n\nexport const updateMapSchema = z.object({\n title: mapTitle,\n description: mapDescription.optional(),\n userPublic: z.boolean().optional(),\n partnerPublic: z.boolean().optional(),\n url: z.string().url().nullable().optional(),\n})\n\nexport const reorderMapSchema = z.object({\n groups: z.array(\n z.object({\n id: z.string().uuid().nullable(),\n position: z.number(),\n }),\n ),\n places: z.array(\n z.object({\n id: z.string(),\n groupId: z.string().uuid().nullable(),\n position: z.number(),\n }),\n ),\n})\n\nexport const updatePlaceFormSchema = z.object({\n id: z.string(),\n name: placeName,\n description: placeDescription.optional(),\n hidden: z.boolean(),\n})\n\nexport const searchSchema = z.object({\n query: mapCreationQuery,\n})\n\nexport const createGroupSchema = z.object({\n name: groupName,\n description: groupDescription.optional(),\n parentGroupId: z.string().optional(),\n color: groupColor.optional(),\n})\n\nexport const editGroupSchema = z.object({\n id: z.string().uuid(),\n color: groupColor.nullable(),\n hidden: z.boolean(),\n name: groupName,\n description: groupDescription.optional(),\n})\n\n// --- Place metadata (lightweight projections) --------------------------------\n\nexport const placeMeta = z.object({\n id: z.string(),\n updatedAt: z.coerce.date(),\n groupId: z.string().nullable(),\n position: z.number().nullable(),\n error: z.string().nullable(),\n lat: z.number().nullable(),\n lon: z.number().nullable(),\n name: z.string().nullable(),\n})\nexport type PlaceMeta = z.infer<typeof placeMeta>\n\nexport const positionedPlaceMeta = placeMeta.extend({\n position: z.number(),\n})\nexport type PositionedPlaceMeta = z.infer<typeof positionedPlaceMeta>\n\nexport const mappablePlaceMeta = placeMeta.extend({\n lat: z.number(),\n lon: z.number(),\n error: z.null(),\n})\nexport type MappablePlaceMeta = z.infer<typeof mappablePlaceMeta>\n\n/**\n * Filter a list of place projections down to those that are mappable\n * (have coordinates and no error).\n */\nexport function getMappablePlaceMetas(\n places: (PlaceMeta | null | undefined)[],\n): MappablePlaceMeta[] {\n const good: MappablePlaceMeta[] = []\n for (const p of places) {\n const parsed = mappablePlaceMeta.safeParse(p)\n if (parsed.success) good.push(parsed.data)\n }\n return good\n}\n","import type { GetLocationsResponse } from \"../ai\"\nimport type { ParsedLocation } from \"../types/domain\"\nimport { placesFromList, placesFromText, placesFromUrl } from \"../types/schemas\"\nimport type {\n GenerateFromSourceOptions,\n GenerateFromSourceResult,\n LlmUsage,\n MapGenerator,\n MapGeneratorConfig,\n} from \"./types\"\n\n/**\n * Default titles when the LLM does not return one. These match what the\n * private app has historically used so migrated data stays consistent.\n */\nconst DEFAULT_TITLES: Record<GenerateFromSourceOptions[\"sourceType\"], string> = {\n url: \"Map from Site\",\n text: \"Map from Text\",\n list: \"Map from List\",\n}\n\nfunction toUsage(r: GetLocationsResponse): LlmUsage {\n const { output: _output, ...rest } = r\n return rest\n}\n\n/**\n * Create a {@link MapGenerator} that wires a {@link LocationParser} and\n * {@link GeocodingProvider} together.\n *\n * @example\n * ```ts\n * import { createLocationParser } from \"mapthis/ai\"\n * import { createGeocoder } from \"mapthis/geocoding\"\n * import { createMapGenerator } from \"mapthis/generate\"\n *\n * const generator = createMapGenerator({\n * parser: createLocationParser({ apiKey: process.env.OPENAI_API_KEY! }),\n * geocoder: createGeocoder({ provider: \"google\", apiKey: process.env.GOOGLE_MAPS_KEY! }),\n * })\n *\n * const result = await generator.generateFromSource({\n * sourceType: \"url\",\n * source: \"https://example.com/best-restaurants-tokyo\",\n * })\n *\n * // Persist in your own DB:\n * await db.$transaction(async (tx) => {\n * const map = await tx.map.create({ data: { title: result.title, ... } })\n * const group = await tx.placeGroup.create({ data: { mapId: map.id, ... } })\n * await tx.place.createMany({\n * data: result.places.map((p, i) => ({\n * mapId: map.id,\n * groupId: group.id,\n * position: i,\n * query: p.input.address,\n * description: p.input.description,\n * name: p.data?.name,\n * address: p.data?.address,\n * lat: p.data?.lat,\n * lon: p.data?.lng,\n * provider: p.data?.provider,\n * providerId: p.data?.providerId,\n * error: p.error,\n * locationPromptVersion: result.locationPromptVersion,\n * summaryPromptVersion: result.summaryPromptVersion,\n * })),\n * })\n * })\n * ```\n */\nexport function createMapGenerator(config: MapGeneratorConfig): MapGenerator {\n const { parser, geocoder } = config\n\n return {\n async generateFromSource(\n options: GenerateFromSourceOptions,\n ): Promise<GenerateFromSourceResult> {\n let parsedLocs: ParsedLocation[] = []\n let title: string = DEFAULT_TITLES[options.sourceType]\n let usage: LlmUsage | undefined = undefined\n let locationPromptVersion: string | undefined = undefined\n let summaryPromptVersion: string | undefined = undefined\n\n switch (options.sourceType) {\n case \"url\": {\n const url = placesFromUrl.parse(options.source)\n const llm = await parser.parseLocationsFromUrl(url)\n parsedLocs = llm.output.locations\n if (llm.output.title) title = llm.output.title\n locationPromptVersion = llm.locationPromptVersion\n summaryPromptVersion = llm.summaryPromptVersion\n usage = toUsage(llm)\n break\n }\n case \"text\": {\n const text = placesFromText.parse(options.source)\n const llm = await parser.parseLocationsFromText(text)\n parsedLocs = llm.output.locations\n if (llm.output.title) title = llm.output.title\n locationPromptVersion = llm.locationPromptVersion\n summaryPromptVersion = llm.summaryPromptVersion\n usage = toUsage(llm)\n break\n }\n case \"list\": {\n const list = placesFromList.parse(options.source)\n parsedLocs = list\n .trim()\n .split(\"\\n\")\n .map((line) => ({ address: line, description: \"\" }))\n break\n }\n default: {\n const _exhaustive: never = options\n throw new Error(\n `Unsupported sourceType: ${(_exhaustive as { sourceType: string }).sourceType}`,\n )\n }\n }\n\n const places = await geocoder.geocodeMany(parsedLocs)\n\n return {\n title,\n places,\n sourceType: options.sourceType,\n source: options.source,\n locationPromptVersion,\n summaryPromptVersion,\n usage,\n }\n },\n }\n}\n"]}
|
|
1
|
+
{"version":3,"sources":["../../src/types/schemas.ts","../../src/generate/generator.ts"],"names":["z"],"mappings":"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","file":"index.cjs","sourcesContent":["import { z } from \"zod\"\n\n/**\n * Runtime validation schemas and their inferred types.\n *\n * These schemas define the public contract between producers and consumers of\n * mapthis data. They are intentionally framework-agnostic — no Prisma, no\n * Next.js, no auth assumptions.\n */\n\n// --- Providers & source types ------------------------------------------------\n\nexport const llmProvider = z.union([z.literal(\"openai\"), z.literal(\"anthropic\")])\nexport type LlmProvider = z.infer<typeof llmProvider>\n\nexport const placeProvider = z.union([z.literal(\"google\"), z.literal(\"locationiq\")])\nexport type PlaceProvider = z.infer<typeof placeProvider>\n\nexport const generationSourceType = z.union([\n z.literal(\"blank\"),\n z.literal(\"autocomplete\"),\n z.literal(\"url\"),\n z.literal(\"text\"),\n z.literal(\"list\"),\n])\nexport type GenerationSourceType = z.infer<typeof generationSourceType>\n\n// --- Geographic primitives ---------------------------------------------------\n\nexport const latitude = z.coerce.number().min(-90, \"Min latitude is -90\").max(90, \"Max latitude is 90\")\nexport const longitude = z.coerce.number().min(-180, \"Min longitude is -180\").max(180, \"Max longitude is 180\")\n\nexport const coordinates = z.object({\n lat: latitude,\n lng: longitude,\n})\nexport type Coordinates = z.infer<typeof coordinates>\n\n// --- Input schemas (freeform sources) ----------------------------------------\n\nexport const placesFromUrl = z.string().url({ message: \"Invalid url\" }).min(6).max(2048)\n\nexport const placesFromList = z\n .preprocess(\n (v) =>\n String(v)\n .split(\"\\n\")\n .map((line) => line.trim())\n .filter((line) => !!line)\n .join(\"\\n\"),\n z.string().min(6, \"Must be at least 6 characters\").max(1000, \"List too long\"),\n )\n .refine(\n (v) => !v.split(\"\\n\").find((line) => line.length < 2),\n \"All lines must be at least 2 characters\",\n )\n\nexport const placesFromText = z\n .string()\n .min(6, \"Must be at least 6 characters\")\n .max(2000, \"Over character limit\")\n\n// --- Map field schemas -------------------------------------------------------\n\nexport const mapExternalId = z.string().min(6).max(100)\nexport const mapTitle = z\n .string()\n .min(3, { message: \"Title must be at least 3 characters\" })\n .max(100, { message: \"Title must be at most 100 characters\" })\nexport const mapDescription = z.string().max(1000, { message: \"Limit is 1000 characters\" })\nexport const mapCreationQuery = z\n .string()\n .min(10, \"Minimum 10 characters for search\")\n .max(200, \"Maximum 200 characters\")\n\n// --- Group field schemas -----------------------------------------------------\n\nexport const groupName = z\n .string()\n .min(3, { message: \"Name must be at least 3 characters\" })\n .max(200, { message: \"Name must be at most 200 characters\" })\nexport const groupDescription = z.string().max(400, { message: \"Limit is 400 characters\" })\nexport const groupColor = z.string().startsWith(\"#\").length(7)\n\n// --- Place field schemas -----------------------------------------------------\n\nexport const placeName = z\n .string()\n .min(2, { message: \"Name must be at least 2 characters\" })\n .max(200, { message: \"Name must be at most 200 characters\" })\nexport const placeDescription = z.string().max(400, { message: \"Limit is 400 characters\" })\n\n// --- Map creation composites -------------------------------------------------\n\nexport const createMapBase = z.object({\n title: mapTitle.optional(),\n description: mapDescription.optional(),\n externalId: mapExternalId.optional(),\n})\n\nexport const createMapSchema = createMapBase.extend({\n sourceType: generationSourceType,\n source: z.string().optional(),\n})\n\nexport const createBlankMapSchema = createMapBase\n\nexport const createMapFromUrlSchema = createMapBase.extend({\n url: placesFromUrl,\n})\nexport const createMapFromListSchema = createMapBase.extend({\n list: placesFromList,\n})\nexport const createMapFromTextSchema = createMapBase.extend({\n text: placesFromText,\n})\nexport const createMapFromQueryOrUrlSchema = createMapBase.extend({\n queryOrUrl: z.union([mapCreationQuery, placesFromUrl]),\n})\n\n// --- Adding places -----------------------------------------------------------\n\nexport const addPlacesBaseSchema = z.object({\n sourceType: generationSourceType,\n source: z.string(),\n})\n\nexport const addPlacesFromSourceSchema = addPlacesBaseSchema.extend({\n mapId: z.string(),\n groupId: z.string().optional(),\n})\n\nexport const addPlacesFromUrlSchema = z.object({\n url: placesFromUrl,\n})\nexport const addPlacesFromListSchema = z.object({\n list: placesFromList,\n})\nexport const addPlacesFromTextSchema = z.object({\n text: placesFromText,\n})\n\nexport const providerAutocompletePlace = z.object({\n query: z.string(),\n provider: z.literal(\"google\"),\n providerId: z.string(),\n name: z.string(),\n description: z.string().optional(),\n address: z.string(),\n lat: z.number().min(-90).max(90),\n lng: z.number().min(-180).max(180),\n})\nexport type ProviderAutocompletePlace = z.infer<typeof providerAutocompletePlace>\n\nexport const addPlacesFromClientAutocompleteSchema = providerAutocompletePlace.extend({\n mapId: z.string(),\n groupId: z.string().optional(),\n})\n\n// --- Updates -----------------------------------------------------------------\n\nexport const updateMapSchema = z.object({\n title: mapTitle,\n description: mapDescription.optional(),\n userPublic: z.boolean().optional(),\n partnerPublic: z.boolean().optional(),\n url: z.string().url().nullable().optional(),\n})\n\nexport const reorderMapSchema = z.object({\n groups: z.array(\n z.object({\n id: z.string().uuid().nullable(),\n position: z.number(),\n }),\n ),\n places: z.array(\n z.object({\n id: z.string(),\n groupId: z.string().uuid().nullable(),\n position: z.number(),\n }),\n ),\n})\n\nexport const updatePlaceFormSchema = z.object({\n id: z.string(),\n name: placeName,\n description: placeDescription.optional(),\n hidden: z.boolean(),\n})\n\nexport const searchSchema = z.object({\n query: mapCreationQuery,\n})\n\nexport const createGroupSchema = z.object({\n name: groupName,\n description: groupDescription.optional(),\n parentGroupId: z.string().optional(),\n color: groupColor.optional(),\n})\n\nexport const editGroupSchema = z.object({\n id: z.string().uuid(),\n color: groupColor.nullable(),\n hidden: z.boolean(),\n name: groupName,\n description: groupDescription.optional(),\n})\n\n// --- Place metadata (lightweight projections) --------------------------------\n\nexport const placeMeta = z.object({\n id: z.string(),\n updatedAt: z.coerce.date(),\n groupId: z.string().nullable(),\n position: z.number().nullable(),\n error: z.string().nullable(),\n lat: z.number().nullable(),\n lon: z.number().nullable(),\n name: z.string().nullable(),\n})\nexport type PlaceMeta = z.infer<typeof placeMeta>\n\nexport const positionedPlaceMeta = placeMeta.extend({\n position: z.number(),\n})\nexport type PositionedPlaceMeta = z.infer<typeof positionedPlaceMeta>\n\nexport const mappablePlaceMeta = placeMeta.extend({\n lat: z.number(),\n lon: z.number(),\n error: z.null(),\n})\nexport type MappablePlaceMeta = z.infer<typeof mappablePlaceMeta>\n\n/**\n * Filter a list of place projections down to those that are mappable\n * (have coordinates and no error).\n */\nexport function getMappablePlaceMetas(\n places: (PlaceMeta | null | undefined)[],\n): MappablePlaceMeta[] {\n const good: MappablePlaceMeta[] = []\n for (const p of places) {\n const parsed = mappablePlaceMeta.safeParse(p)\n if (parsed.success) good.push(parsed.data)\n }\n return good\n}\n","import type { GetLocationsResponse } from \"../ai\"\nimport type { ParsedLocation } from \"../types/domain\"\nimport { placesFromList, placesFromText, placesFromUrl } from \"../types/schemas\"\nimport type {\n GenerateFromSourceOptions,\n GenerateFromSourceResult,\n LlmUsage,\n MapGenerator,\n MapGeneratorConfig,\n} from \"./types\"\n\n/**\n * Default titles when the LLM does not return one. These match what the\n * private app has historically used so migrated data stays consistent.\n */\nconst DEFAULT_TITLES: Record<GenerateFromSourceOptions[\"sourceType\"], string> = {\n url: \"Map from Site\",\n text: \"Map from Text\",\n list: \"Map from List\",\n}\n\nfunction toUsage(r: GetLocationsResponse): LlmUsage {\n const { output: _output, ...rest } = r\n return rest\n}\n\n/**\n * Create a {@link MapGenerator} that wires a {@link LocationParser} and\n * {@link GeocodingProvider} together.\n *\n * @example\n * ```ts\n * import { createLocationParser } from \"mapthis/ai\"\n * import { createGeocoder } from \"mapthis/geocoding\"\n * import { createMapGenerator } from \"mapthis/generate\"\n *\n * const generator = createMapGenerator({\n * parser: createLocationParser({ apiKey: process.env.OPENAI_API_KEY! }),\n * geocoder: createGeocoder({ provider: \"google\", apiKey: process.env.GOOGLE_MAPS_KEY! }),\n * })\n *\n * const result = await generator.generateFromSource({\n * sourceType: \"url\",\n * source: \"https://example.com/best-restaurants-tokyo\",\n * })\n *\n * // Persist in your own DB:\n * await db.$transaction(async (tx) => {\n * const map = await tx.map.create({ data: { title: result.title, ... } })\n * const group = await tx.placeGroup.create({ data: { mapId: map.id, ... } })\n * await tx.place.createMany({\n * data: result.places.map((p, i) => ({\n * mapId: map.id,\n * groupId: group.id,\n * position: i,\n * query: p.input.address,\n * description: p.input.description,\n * name: p.data?.name,\n * address: p.data?.address,\n * lat: p.data?.lat,\n * lon: p.data?.lng,\n * provider: p.data?.provider,\n * providerId: p.data?.providerId,\n * error: p.error,\n * locationPromptVersion: result.locationPromptVersion,\n * summaryPromptVersion: result.summaryPromptVersion,\n * })),\n * })\n * })\n * ```\n */\nexport function createMapGenerator(config: MapGeneratorConfig): MapGenerator {\n const { parser, geocoder } = config\n\n return {\n async generateFromSource(\n options: GenerateFromSourceOptions,\n ): Promise<GenerateFromSourceResult> {\n let parsedLocs: ParsedLocation[] = []\n let title: string = DEFAULT_TITLES[options.sourceType]\n let usage: LlmUsage | undefined = undefined\n let locationPromptVersion: string | undefined = undefined\n let summaryPromptVersion: string | undefined = undefined\n\n switch (options.sourceType) {\n case \"url\": {\n const url = placesFromUrl.parse(options.source)\n const llm = await parser.parseLocationsFromUrl(url)\n parsedLocs = llm.output.locations\n if (llm.output.title) title = llm.output.title\n locationPromptVersion = llm.locationPromptVersion\n summaryPromptVersion = llm.summaryPromptVersion\n usage = toUsage(llm)\n break\n }\n case \"text\": {\n const text = placesFromText.parse(options.source)\n const llm = await parser.parseLocationsFromText(text)\n parsedLocs = llm.output.locations\n if (llm.output.title) title = llm.output.title\n locationPromptVersion = llm.locationPromptVersion\n summaryPromptVersion = llm.summaryPromptVersion\n usage = toUsage(llm)\n break\n }\n case \"list\": {\n const list = placesFromList.parse(options.source)\n parsedLocs = list\n .trim()\n .split(\"\\n\")\n .map((line) => ({ address: line, description: \"\" }))\n break\n }\n default: {\n const _exhaustive: never = options\n throw new Error(\n `Unsupported sourceType: ${(_exhaustive as { sourceType: string }).sourceType}`,\n )\n }\n }\n\n const places = await geocoder.geocodeMany(parsedLocs)\n\n return {\n title,\n places,\n sourceType: options.sourceType,\n source: options.source,\n locationPromptVersion,\n summaryPromptVersion,\n usage,\n }\n },\n }\n}\n"]}
|
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
import { G as GetLocationsResponse, L as LocationParser } from '../parser-
|
|
2
|
-
import { G as GeocodingProvider } from '../types-
|
|
3
|
-
import { i as PlaceQueryResult } from '../domain-
|
|
1
|
+
import { G as GetLocationsResponse, L as LocationParser } from '../parser-DsIwIsHK.cjs';
|
|
2
|
+
import { G as GeocodingProvider } from '../types-C-xqh9Sh.cjs';
|
|
3
|
+
import { i as PlaceQueryResult } from '../domain-CL4ro2YW.cjs';
|
|
4
4
|
import 'json-schema';
|
|
5
|
-
import '../schemas-
|
|
5
|
+
import '../schemas-CZao45EU.cjs';
|
|
6
6
|
import 'zod';
|
|
7
7
|
|
|
8
8
|
/**
|
package/dist/generate/index.d.ts
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
import { G as GetLocationsResponse, L as LocationParser } from '../parser-
|
|
2
|
-
import { G as GeocodingProvider } from '../types-
|
|
3
|
-
import { i as PlaceQueryResult } from '../domain-
|
|
1
|
+
import { G as GetLocationsResponse, L as LocationParser } from '../parser-CrG1A6dD.js';
|
|
2
|
+
import { G as GeocodingProvider } from '../types-BhPqZwOj.js';
|
|
3
|
+
import { i as PlaceQueryResult } from '../domain-Dh8cPBVp.js';
|
|
4
4
|
import 'json-schema';
|
|
5
|
-
import '../schemas-
|
|
5
|
+
import '../schemas-CZao45EU.js';
|
|
6
6
|
import 'zod';
|
|
7
7
|
|
|
8
8
|
/**
|
package/dist/generate/index.js
CHANGED