npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2025 – Pkg Stats / Ryan Hefner

@amedia/parnassus-client

v1.1.9

Published

Client library for integrating Parnassus templates with LLM API

Readme

@amedia/parnassus-client

En enkel klient for å integrere Parnassus-templates med LLM API. Denne pakken gjør det lett å:

  1. Hente templates fra Parnassus
  2. Fylle ut placeholders med data
  3. Formatere som et LLM API-request
  4. Sende request til LLM API

Installasjon

npm install @amedia/parnassus-client

Grunnleggende bruk

Enkel eksempel

import { ParnassusClient } from '@amedia/parnassus-client';

// Opprett klient
const client = new ParnassusClient();

// Bruk execute() for komplett workflow
const response = await client.execute('task-123', {
  'placeholder-id-1': 'Min tekst her',
  'placeholder-id-2': 'Mer data her',
});

// Hent ut innholdet
const content = response.choices[0].message.content;
console.log(content);

Med JSON-parsing

// For JSON-responser, bruk executeJson()
const jsonResult = await client.executeJson('task-123', {
  'article-text': 'En lang artikkel...',
  'keywords': 'sport, fotball, tippeligaen',
});

// jsonResult er allerede parsed
console.log(jsonResult);

Med egendefinert modell

const response = await client.execute(
  'task-123',
  {
    'input-text': 'Min tekst',
  },
  {
    model: 'gpt-4o' // eller 'o3-mini', 'claude-3-opus', etc.
  }
);

🚧 Utvikling med egne prompts

For utvikling og testing kan du bruke inline prompts uten Parnassus templates. Merk: Flytter til Parnassus templates før produksjon for bedre håndtering og versjonering!

// Enkelt eksempel - for utvikling/testing
const response = await client.executeDev({
  system: 'Du er en hjelpsom assistent som svarer kort og konsist.',
  user: 'Hva er hovedstaden i Norge?',
  model: 'gpt-4o' // valgfri - bruker defaultModel hvis ikke satt
});

// Med JSON response - også for utvikling
const result = await client.executeDevJson({
  system: 'Analyser følgende tekst og returner JSON.',
  user: 'Teksten: "Dette var en fantastisk film!" Sentiment?',
  responseFormat: {
    type: 'json-schema',
    schema: {
      type: 'object',
      properties: {
        sentiment: { type: 'string' },
        confidence: { type: 'number' }
      },
      required: ['sentiment', 'confidence']
    }
  }
});

console.log(result); // { sentiment: "positive", confidence: 0.95 }

⚠️ Produksjon: Opprett templates i Parnassus og bruk execute() for bedre versjonering, testing og vedlikehold.

Denne funksjonen støtter også alle de samme avanserte features:

const response = await client.executeDev({
  system: 'Du er en ekspert på norsk litteratur.',
  user: 'Fortell om Knut Hamsun'
}, {
  timeout: 30000,
  retry: { retries: 3 },
  onProgress: {
    onSendingRequest: () => console.log('Sender dev prompt...')
  }
});

Avanserte features

🔄 Automatisk retry med exponential backoff

Klienten har innebygd automatisk retry for alle nettverks-requests. Dette håndterer transiente feil som nettverksendringer, timeouts og server-feil:

// Standard konfigurasjon (kan overstyres i constructor)
const client = new ParnassusClient({
  fetchRetry: {
    maxRetries: 3,        // Prøv inntil 3 ganger
    initialDelay: 100,    // Start med 100ms delay
    maxDelay: 1000,       // Aldri vent mer enn 1s
    jitter: true          // Legg til tilfeldig jitter
  },
  debug: true             // Logg retry-forsøk
});

// Eksempel på automatisk retry ved ERR_NETWORK_CHANGED:
// [fetchWithRetry] Attempt 1/4 failed with error: ERR_NETWORK_CHANGED. Retrying in 120ms...
// [fetchWithRetry] Attempt 2/4 failed with error: ERR_NETWORK_CHANGED. Retrying in 256ms...
// ✅ Success på forsøk 3!

Retry-logikk:

  • ✅ Retries på: Nettverksfeil (ERR_NETWORK_CHANGED, timeouts, ECONNRESET), 5xx server-feil, 429 Too Many Requests
  • ❌ Ingen retry på: 4xx klient-feil (unntatt 429)
  • 📈 Exponential backoff: 100ms → 200ms → 400ms → 800ms (med jitter)
  • 🎲 Jitter forhindrer "thundering herd" ved samtidig retry

Du kan også konfigurere retry på application-nivå:

const response = await client.execute('task-123', values, {
  retry: {
    retries: 5,                    // Flere forsøk for kritiske operasjoner
    retryDelay: 2000,
    exponentialBackoff: true
  }
});

⏱️ Timeout

Sett en timeout for å unngå at requester henger for lenge:

const response = await client.execute('task-123', values, {
  timeout: 30000, // 30 sekunder
});

🚫 Abort/Cancel

Avbryt en request manuelt:

const controller = new AbortController();

// Start request
const promise = client.execute('task-123', values, {
  signal: controller.signal,
});

// Avbryt når du vil
setTimeout(() => controller.abort(), 5000);

try {
  const response = await promise;
} catch (error) {
  console.log('Request avbrutt');
}

🔄 Retry med exponential backoff

Automatisk retry ved feil:

const response = await client.execute('task-123', values, {
  retry: {
    retries: 3,              // Prøv inntil 3 ganger
    retryDelay: 1000,        // Start med 1 sekund
    exponentialBackoff: true // Doble delay for hver retry
  }
});

Egendefinert retry-logikk:

const response = await client.execute('task-123', values, {
  retry: {
    retries: 5,
    retryDelay: 500,
    shouldRetry: (error, attempt) => {
      // Kun retry på spesifikke feil
      return error.message.includes('rate limit');
    }
  }
});

📊 Progress callbacks

Få tilbakemelding underveis i prosessen:

const response = await client.execute('task-123', values, {
  onProgress: {
    onFetchingTemplate: () => console.log('📥 Henter template...'),
    onTemplateFetched: (template) => console.log('✅ Template hentet:', template.name),
    onFillingTemplate: () => console.log('✏️ Fyller ut template...'),
    onTemplateFilled: (prompt) => console.log('📝 Prompt klar'),
    onSendingRequest: () => console.log('🚀 Sender til Ivar...'),
    onReceivingResponse: () => console.log('⏳ Venter på svar...'),
    onResponseReceived: (response) => console.log('✨ Svar mottatt!'),
    onParsingResponse: () => console.log('🔍 Parser JSON...')
  }
});

🌊 Streaming

For å få innhold underveis mens LLM genererer:

for await (const chunk of client.executeStream('task-123', values)) {
  if (chunk.type === 'content') {
    process.stdout.write(chunk.content);
  } else if (chunk.type === 'done') {
    console.log('\n✅ Ferdig!');
  } else if (chunk.type === 'error') {
    console.error('❌ Feil:', chunk.error);
  }
}

Med progress callbacks og timeout:

for await (const chunk of client.executeStream('task-123', values, {
  timeout: 60000,
  onProgress: {
    onSendingRequest: () => console.log('Starter streaming...')
  }
})) {
  // Håndter chunks
}

🎯 Kombinert eksempel

Alt sammen:

const controller = new AbortController();

try {
  const response = await client.execute('task-123', {
    'article': 'Lang tekst...'
  }, {
    model: 'gpt-4o',
    timeout: 30000,
    signal: controller.signal,
    retry: {
      retries: 3,
      retryDelay: 1000,
      exponentialBackoff: true
    },
    onProgress: {
      onFetchingTemplate: () => console.log('Starter...'),
      onSendingRequest: () => console.log('Sender request...'),
      onResponseReceived: () => console.log('Svar mottatt!')
    }
  });
  
  console.log('Resultat:', response.choices[0].message.content);
} catch (error) {
  if (error instanceof TemplateNotFoundError) {
    console.error('Template finnes ikke');
  } else if (error instanceof LLMApiError) {
    console.error('LLM API feil:', error.statusCode);
  }
}

Avansert bruk

Steg-for-steg

For mer kontroll kan du bruke de individuelle metodene:

const client = new ParnassusClient();

// 1. Hent template
const template = await client.getTemplate('task-123');

// 2. Fyll ut template med dine verdier
const filledPrompt = client.fillTemplate(template, {
  'article-text': 'Min artikkel...',
  'tone': 'formell',
});

// 3. Opprett LLM request objekt
const llmRequest = client.createLLMRequest(template, filledPrompt, 'gpt-4o');

// 4. Send til LLM API
const response = await client.sendRequest(llmRequest);

// 5. Parse JSON hvis nødvendig
if (template.responseFormat.type === 'json-schema') {
  const jsonData = client.parseJsonResponse(response);
  console.log(jsonData);
}

Egendefinert konfigurasjon

const client = new ParnassusClient({
  parnassusBaseUrl: 'https://services.api.no/api/parnassus/v1',
  llmApiUrl: 'https://services.api.no/api/auris/v1/ivar',
  defaultModel: 'gpt-4o',
});
import { ParnassusClient } from '@amedia/parnassus-client';

async function summarizeArticle(articleText: string) {
  const client = new ParnassusClient();

  try {
    // Hent og kjør template i ett kall
    const response = await client.executeJson('summary-task-42', {
      'article-text': articleText,
    });

    // response er allerede parsed JSON
    console.log('Sammendrag:', response.summary);
    console.log('Nøkkelord:', response.keywords);

    return response;
  } catch (error) {
    console.error('Feil ved sammendrag:', error);
    throw error;
  }
}

// Bruk
const result = await summarizeArticle('En lang artikkel om...');

API

ParnassusClient

Constructor

new ParnassusClient(config?: ParnassusClientConfig)

Config options:

  • parnassusBaseUrl?: string - Base URL for Parnassus API (default: 'https://services.api.no/api/parnassus/v1')
  • llmApiUrl?: string - Base URL for LLM API (default: 'https://services.api.no/api/auris/v1/ivar')
  • defaultModel?: string - Default LLM model (default: 'gpt-4o')
  • fetchRetry?: FetchRetryConfig - Configuration for automatic retry of failed requests
    • maxRetries?: number - Maximum retry attempts (default: 3)
    • initialDelay?: number - Initial delay before first retry in ms (default: 100)
    • maxDelay?: number - Maximum delay between retries in ms (default: 1000)
    • jitter?: boolean - Add random jitter to prevent thundering herd (default: true)
  • debug?: boolean - Enable debug logging including retry attempts (default: false)
  • headers?: Record<string, string> - Custom headers to include in all requests
  • suppressDevWarning?: boolean - Suppress warning when using dev methods (default: false)

Metoder

execute(taskId, values, options?)

Komplett workflow i ett kall.

  • taskId: string | number - ID til Parnassus task
  • values: PlaceholderValues - Object med placeholder-verdier
  • options: ExecuteOptions? - Valgfrie innstillinger
    • model?: string - Override default model
    • timeout?: number - Timeout i millisekunder
    • signal?: AbortSignal - Abort signal for cancellation
    • retry?: RetryOptions - Retry konfigurasjon
      • retries?: number - Antall retry-forsøk (default: 0)
      • retryDelay?: number - Delay mellom retries i ms (default: 1000)
      • exponentialBackoff?: boolean - Bruk exponential backoff (default: true)
      • shouldRetry?: (error, attempt) => boolean - Custom retry-logikk
    • onProgress?: ProgressCallbacks - Callbacks for fremgang
      • onFetchingTemplate?: () => void
      • onTemplateFetched?: (template) => void
      • onFillingTemplate?: () => void
      • onTemplateFilled?: (prompt) => void
      • onSendingRequest?: () => void
      • onReceivingResponse?: () => void
      • onResponseReceived?: (response) => void
      • onParsingResponse?: () => void
  • Returns: Promise<LLMResponse>
executeJson(taskId, values, options?)

Som execute(), men parser JSON automatisk.

  • Returns: Promise<unknown> - Parsed JSON fra response
executeStream(taskId, values, options?)

Streaming versjon - yield chunks underveis.

  • Returns: AsyncGenerator<StreamChunk> - Stream av chunks
    • Chunk types: 'content' | 'done' | 'error'
getTemplate(taskId, signal?)

Hent en template fra Parnassus.

  • taskId: string | number - ID til Parnassus task
  • signal: AbortSignal? - Valgfri abort signal
  • Returns: Promise<ParnassusTemplate>
fillTemplate(template, values)

Fyll ut placeholders i en template.

  • template: ParnassusTemplate - Template fra Parnassus
  • values: PlaceholderValues - Verdier for hver placeholder
  • Returns: string - Ferdig utfylt prompt
createLLMRequest(template, userPrompt, model?)

Opprett et LLM request objekt.

  • template: ParnassusTemplate - Template med system prompt og response format
  • userPrompt: string - Ferdig utfylt user prompt
  • model: string? - Valgfri modell
  • Returns: LLMRequest
sendRequest(llmRequest, signal?)

Send request til LLM API.

  • llmRequest: LLMRequest - Request objekt
  • signal: AbortSignal? - Valgfri abort signal
  • Returns: Promise<LLMResponse>
parseJsonResponse(llmResponse)

Parse JSON fra LLM response (håndterer både raw JSON og markdown-wrapped JSON).

  • llmResponse: LLMResponse - Response fra LLM API
  • Returns: unknown - Parsed JSON
executeDev(prompt, options?)

Kjør inline prompts uten Parnassus template (kun for utvikling/testing).

  • prompt: DevPrompt - Dev prompt definisjon
    • system?: string - Valgfri system prompt
    • user: string - User prompt (påkrevd)
    • model?: string - Valgfri modell override
    • responseFormat?: object - Valgfri JSON schema for response
  • options: Omit<ExecuteOptions, 'model'> - Alle ExecuteOptions unntatt model
  • Returns: Promise<LLMResponse>
  • ⚠️ Merk: Flytt til Parnassus templates før produksjon!
executeDevJson(prompt, options?)

Som executeDev(), men parser JSON automatisk.

  • Returns: Promise<unknown> - Parsed JSON fra response

TypeScript

Pakken er skrevet i TypeScript og inkluderer alle type-definisjoner. Du får full IntelliSense og type-checking.

import type {
  ParnassusTemplate,
  LLMRequest,
  LLMResponse,
  PlaceholderValues,
  ExecuteOptions,
  StreamChunk,
  ProgressCallbacks,
  RetryOptions,
} from '@amedia/parnassus-client';

Features

Enkel API - Ett kall for hele workflowen
TypeScript - Full type-safety og IntelliSense
Automatisk retry - Håndterer transiente nettverksfeil (ERR_NETWORK_CHANGED, timeouts, 5xx)
Exponential backoff - Smart retry-strategi med jitter for å unngå overbelastning
Konfigurerbar retry - Tilpass retry-logikk for dine behov
Timeout support - Avbryt requests som tar for lang tid
Abort/Cancel - Manuell avbrytelse av requests
Progress callbacks - Få tilbakemelding underveis
Streaming - Motta innhold mens det genereres
Error handling - Spesifikke error-typer for bedre håndtering
Validering - Validerer templates før bruk
JSON parsing - Håndterer både raw og markdown-wrapped JSON
Debug logging - Detaljert logging av retry-forsøk og API-kall

Feilhåndtering

Pakken eksporterer spesifikke error-typer for bedre feilhåndtering:

import { 
  ParnassusClient,
  TemplateNotFoundError,
  LLMApiError,
  TemplateValidationError 
} from '@amedia/parnassus-client';

try {
  const response = await client.execute('task-123', { input: 'test' });
} catch (error) {
  if (error instanceof TemplateNotFoundError) {
    console.error('Template finnes ikke (404)');
  } else if (error instanceof LLMApiError) {
    console.error('LLM API feil:', error.statusCode, error.message);
  } else if (error instanceof TemplateValidationError) {
    console.error('Ugyldig template struktur');
  } else if (error.name === 'AbortError') {
    console.error('Request ble avbrutt');
  }
}

Lisens

MIT