@probeo/anymodel 0.3.1 → 0.4.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 CHANGED
@@ -81,6 +81,7 @@ mistral/mistral-large-latest
81
81
  groq/llama-3.3-70b-versatile
82
82
  deepseek/deepseek-chat
83
83
  xai/grok-3
84
+ perplexity/sonar-pro
84
85
  ollama/llama3.3
85
86
  ```
86
87
 
@@ -436,6 +437,11 @@ npx tsx examples/basic.ts batch
436
437
  - [ ] **Result export** — `saveResults()` to write batch results to a configurable output directory
437
438
  - [ ] **Prompt logging** — optional request/response logging for debugging and evaluation
438
439
 
440
+ ## Also Available
441
+
442
+ - **Python**: [`anymodel-py`](https://github.com/probeo-io/anymodel-py) on PyPI
443
+ - **Go**: [`anymodel-go`](https://github.com/probeo-io/anymodel-go)
444
+
439
445
  ## License
440
446
 
441
447
  MIT
package/dist/cli.cjs CHANGED
@@ -1370,6 +1370,190 @@ function createGoogleAdapter(apiKey) {
1370
1370
  return adapter;
1371
1371
  }
1372
1372
 
1373
+ // src/providers/perplexity.ts
1374
+ var PERPLEXITY_API_BASE = "https://api.perplexity.ai";
1375
+ var SUPPORTED_PARAMS4 = /* @__PURE__ */ new Set([
1376
+ "temperature",
1377
+ "max_tokens",
1378
+ "top_p",
1379
+ "frequency_penalty",
1380
+ "presence_penalty",
1381
+ "stream",
1382
+ "stop",
1383
+ "response_format",
1384
+ "tools",
1385
+ "tool_choice"
1386
+ ]);
1387
+ var MODELS = [
1388
+ { id: "sonar", name: "Sonar", context: 128e3, maxOutput: 4096, modality: "text->text", inputModalities: ["text"] },
1389
+ { id: "sonar-pro", name: "Sonar Pro", context: 2e5, maxOutput: 8192, modality: "text->text", inputModalities: ["text"] },
1390
+ { id: "sonar-reasoning", name: "Sonar Reasoning", context: 128e3, maxOutput: 8192, modality: "text->text", inputModalities: ["text"] },
1391
+ { id: "sonar-reasoning-pro", name: "Sonar Reasoning Pro", context: 128e3, maxOutput: 16384, modality: "text->text", inputModalities: ["text"] },
1392
+ { id: "sonar-deep-research", name: "Sonar Deep Research", context: 128e3, maxOutput: 16384, modality: "text->text", inputModalities: ["text"] },
1393
+ { id: "r1-1776", name: "R1 1776", context: 128e3, maxOutput: 16384, modality: "text->text", inputModalities: ["text"] }
1394
+ ];
1395
+ function createPerplexityAdapter(apiKey) {
1396
+ async function makeRequest(path2, body, method = "POST") {
1397
+ const res = await fetch(`${PERPLEXITY_API_BASE}${path2}`, {
1398
+ method,
1399
+ headers: {
1400
+ "Content-Type": "application/json",
1401
+ "Authorization": `Bearer ${apiKey}`
1402
+ },
1403
+ body: body ? JSON.stringify(body) : void 0
1404
+ });
1405
+ if (!res.ok) {
1406
+ let errorBody;
1407
+ try {
1408
+ errorBody = await res.json();
1409
+ } catch {
1410
+ errorBody = { message: res.statusText };
1411
+ }
1412
+ const msg = errorBody?.error?.message || errorBody?.message || res.statusText;
1413
+ throw new AnyModelError(mapErrorCode(res.status), msg, {
1414
+ provider_name: "perplexity",
1415
+ raw: errorBody
1416
+ });
1417
+ }
1418
+ return res;
1419
+ }
1420
+ function mapErrorCode(status) {
1421
+ if (status === 401 || status === 403) return 401;
1422
+ if (status === 429) return 429;
1423
+ if (status === 400 || status === 422) return 400;
1424
+ if (status >= 500) return 502;
1425
+ return status;
1426
+ }
1427
+ function rePrefixId(id) {
1428
+ if (id && id.startsWith("chatcmpl-")) {
1429
+ return `gen-${id.substring(9)}`;
1430
+ }
1431
+ return id.startsWith("gen-") ? id : `gen-${id}`;
1432
+ }
1433
+ function buildRequestBody(request) {
1434
+ const body = {
1435
+ model: request.model,
1436
+ messages: request.messages
1437
+ };
1438
+ if (request.temperature !== void 0) body.temperature = request.temperature;
1439
+ if (request.max_tokens !== void 0) body.max_tokens = request.max_tokens;
1440
+ if (request.top_p !== void 0) body.top_p = request.top_p;
1441
+ if (request.frequency_penalty !== void 0) body.frequency_penalty = request.frequency_penalty;
1442
+ if (request.presence_penalty !== void 0) body.presence_penalty = request.presence_penalty;
1443
+ if (request.stop !== void 0) body.stop = request.stop;
1444
+ if (request.stream !== void 0) body.stream = request.stream;
1445
+ if (request.response_format !== void 0) body.response_format = request.response_format;
1446
+ if (request.tools !== void 0) body.tools = request.tools;
1447
+ if (request.tool_choice !== void 0) body.tool_choice = request.tool_choice;
1448
+ return body;
1449
+ }
1450
+ const adapter = {
1451
+ name: "perplexity",
1452
+ translateRequest(request) {
1453
+ return buildRequestBody(request);
1454
+ },
1455
+ translateResponse(response) {
1456
+ const r = response;
1457
+ const result = {
1458
+ id: rePrefixId(r.id),
1459
+ object: "chat.completion",
1460
+ created: r.created,
1461
+ model: `perplexity/${r.model}`,
1462
+ choices: r.choices,
1463
+ usage: r.usage
1464
+ };
1465
+ if (r.citations && result.choices?.[0]?.message) {
1466
+ result.citations = r.citations;
1467
+ }
1468
+ return result;
1469
+ },
1470
+ async *translateStream(stream) {
1471
+ const reader = stream.getReader();
1472
+ const decoder = new TextDecoder();
1473
+ let buffer = "";
1474
+ try {
1475
+ while (true) {
1476
+ const { done, value } = await reader.read();
1477
+ if (done) break;
1478
+ buffer += decoder.decode(value, { stream: true });
1479
+ const lines = buffer.split("\n");
1480
+ buffer = lines.pop() || "";
1481
+ for (const line of lines) {
1482
+ const trimmed = line.trim();
1483
+ if (!trimmed || trimmed.startsWith(":")) continue;
1484
+ if (trimmed === "data: [DONE]") return;
1485
+ if (trimmed.startsWith("data: ")) {
1486
+ const json = JSON.parse(trimmed.substring(6));
1487
+ json.id = rePrefixId(json.id);
1488
+ json.model = `perplexity/${json.model}`;
1489
+ yield json;
1490
+ }
1491
+ }
1492
+ }
1493
+ } finally {
1494
+ reader.releaseLock();
1495
+ }
1496
+ },
1497
+ translateError(error) {
1498
+ if (error instanceof AnyModelError) {
1499
+ return { code: error.code, message: error.message, metadata: error.metadata };
1500
+ }
1501
+ const err = error;
1502
+ const status = err?.status || err?.code || 500;
1503
+ return {
1504
+ code: mapErrorCode(status),
1505
+ message: err?.message || "Unknown Perplexity error",
1506
+ metadata: { provider_name: "perplexity", raw: error }
1507
+ };
1508
+ },
1509
+ async listModels() {
1510
+ return MODELS.map((m) => ({
1511
+ id: `perplexity/${m.id}`,
1512
+ name: m.name,
1513
+ created: 0,
1514
+ description: "",
1515
+ context_length: m.context,
1516
+ pricing: { prompt: "0", completion: "0" },
1517
+ architecture: {
1518
+ modality: m.modality,
1519
+ input_modalities: m.inputModalities,
1520
+ output_modalities: ["text"],
1521
+ tokenizer: "unknown"
1522
+ },
1523
+ top_provider: {
1524
+ context_length: m.context,
1525
+ max_completion_tokens: m.maxOutput,
1526
+ is_moderated: false
1527
+ },
1528
+ supported_parameters: Array.from(SUPPORTED_PARAMS4)
1529
+ }));
1530
+ },
1531
+ supportsParameter(param) {
1532
+ return SUPPORTED_PARAMS4.has(param);
1533
+ },
1534
+ supportsBatch() {
1535
+ return false;
1536
+ },
1537
+ async sendRequest(request) {
1538
+ const body = buildRequestBody(request);
1539
+ const res = await makeRequest("/chat/completions", body);
1540
+ const json = await res.json();
1541
+ return adapter.translateResponse(json);
1542
+ },
1543
+ async sendStreamingRequest(request) {
1544
+ const body = buildRequestBody({ ...request, stream: true });
1545
+ const res = await makeRequest("/chat/completions", body);
1546
+ if (!res.body) {
1547
+ throw new AnyModelError(502, "No response body for streaming request", {
1548
+ provider_name: "perplexity"
1549
+ });
1550
+ }
1551
+ return adapter.translateStream(res.body);
1552
+ }
1553
+ };
1554
+ return adapter;
1555
+ }
1556
+
1373
1557
  // src/providers/custom.ts
1374
1558
  function createCustomAdapter(name, config) {
1375
1559
  const openaiAdapter = createOpenAIAdapter(config.apiKey || "", config.baseURL);
@@ -2573,14 +2757,17 @@ var AnyModel = class {
2573
2757
  if (googleKey) {
2574
2758
  this.registry.register("google", createGoogleAdapter(googleKey));
2575
2759
  }
2760
+ const perplexityKey = config.perplexity?.apiKey || process.env.PERPLEXITY_API_KEY;
2761
+ if (perplexityKey) {
2762
+ this.registry.register("perplexity", createPerplexityAdapter(perplexityKey));
2763
+ }
2576
2764
  const builtinProviders = [
2577
2765
  { name: "mistral", baseURL: "https://api.mistral.ai/v1", configKey: "mistral", envVar: "MISTRAL_API_KEY" },
2578
2766
  { name: "groq", baseURL: "https://api.groq.com/openai/v1", configKey: "groq", envVar: "GROQ_API_KEY" },
2579
2767
  { name: "deepseek", baseURL: "https://api.deepseek.com", configKey: "deepseek", envVar: "DEEPSEEK_API_KEY" },
2580
2768
  { name: "xai", baseURL: "https://api.x.ai/v1", configKey: "xai", envVar: "XAI_API_KEY" },
2581
2769
  { name: "together", baseURL: "https://api.together.xyz/v1", configKey: "together", envVar: "TOGETHER_API_KEY" },
2582
- { name: "fireworks", baseURL: "https://api.fireworks.ai/inference/v1", configKey: "fireworks", envVar: "FIREWORKS_API_KEY" },
2583
- { name: "perplexity", baseURL: "https://api.perplexity.ai", configKey: "perplexity", envVar: "PERPLEXITY_API_KEY" }
2770
+ { name: "fireworks", baseURL: "https://api.fireworks.ai/inference/v1", configKey: "fireworks", envVar: "FIREWORKS_API_KEY" }
2584
2771
  ];
2585
2772
  for (const { name, baseURL, configKey, envVar } of builtinProviders) {
2586
2773
  const providerConfig = config[configKey];