nomyo-js/doc/examples.md
alpha nerd 111335d7ff
fix: correct package entry points and publish contents
Five packaging defects, all pre-existing:

1. dist/esm/index.js held ESM syntax while the package is not
   "type": "module", so Node classified it as CommonJS. It failed
   outright on Node 18 ("Unexpected token 'export'") and only worked on
   Node >= 22 because Node re-parses after guessing the module type,
   paying that cost on every import. Bundles now carry explicit
   extensions: .mjs for ES output, .cjs/.js for CommonJS. The browser
   build gained a real CommonJS output too — the exports map previously
   pointed the browser "require" condition at an ES module.
   The exports map now also leads with "types" and ends with a "default"
   fallback for resolvers matching neither "node" nor "browser".

2. files: ["native"] published the local build directory: a 94.6 kB
   Linux-x64 .node binary, a 148 kB object file and generated Makefiles.
   node-gyp-build checks build/Release before prebuilds, so every
   consumer on every platform would have found this machine's binary,
   skipped compiling, and failed to load it. It fails safe (native/
   index.js catches and returns null), but the addon could never work
   for anyone else. Narrowed to the four source files.

3. binding.gyp resolves node-addon-api at build time, but nothing
   declared it: it was a devDependency of the root, absent from
   native/package.json. The build only succeeded here because a dev
   install populates the root node_modules. Declared as a dependency of
   the native package, where it is actually needed.

4. No clean step, so stale output shipped — the tarball carried both
   dist/types/core/** and a dist/types/src/** tree left over from before
   rootDir was set. build now runs clean first.

5. test:browser ran `karma start` with no karma.conf.js anywhere in the
   repo, and tests/browser is an empty directory. Removed the script and
   the karma devDependency rather than leave a script that cannot run.

Verified: CommonJS require and ESM import both resolve on Node 18.19.1
and 24.18.0; TypeScript resolves types under both bundler and node16;
npm pack now produces 35 files / 103.8 kB with no build artefacts.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-19 12:39:09 +02:00

11 KiB

Examples

Basic Usage

Simple Chat

import { SecureChatCompletion } from 'nomyo-js';

const client = new SecureChatCompletion({ apiKey: process.env.NOMYO_API_KEY });

const response = await client.create({
  model: 'Qwen/Qwen3-0.6B',
  messages: [{ role: 'user', content: 'Hello, how are you?' }],
  temperature: 0.7,
});

console.log(response.choices[0].message.content);
client.dispose();

Chat with System Message

const response = await client.create({
  model: 'Qwen/Qwen3-0.6B',
  messages: [
    { role: 'system', content: 'You are a concise technical assistant.' },
    { role: 'user',   content: 'What is the capital of France?' },
  ],
  temperature: 0.7,
});

console.log(response.choices[0].message.content);

Security Tiers

// Standard — general use (GPU)
const r1 = await client.create({
  model: 'Qwen/Qwen3-0.6B',
  messages: [{ role: 'user', content: 'General query' }],
  security_tier: 'standard',
});

// High — sensitive business data
const r2 = await client.create({
  model: 'Qwen/Qwen3-0.6B',
  messages: [{ role: 'user', content: 'Review this contract clause...' }],
  security_tier: 'high',
});

// Maximum — HIPAA PHI / classified data (CPU-only)
const r3 = await client.create({
  model: 'Qwen/Qwen3-0.6B',
  messages: [{ role: 'user', content: 'Patient record summary...' }],
  security_tier: 'maximum',
});

Tool / Function Calling

const response = await client.create({
  model: 'Qwen/Qwen3-0.6B',
  messages: [{ role: 'user', content: "What's the weather in Paris?" }],
  tools: [
    {
      type: 'function',
      function: {
        name: 'get_weather',
        description: 'Get weather information for a location',
        parameters: {
          type: 'object',
          properties: {
            location: { type: 'string', description: 'City name' },
          },
          required: ['location'],
        },
      },
    },
  ],
  tool_choice: 'auto',
});

const message = response.choices[0].message;
if (message.tool_calls?.length) {
  const call = message.tool_calls[0];
  const args = JSON.parse(call.function.arguments);
  console.log(`Call ${call.function.name}(location="${args.location}")`);
  // → Call get_weather(location="Paris")
}

Error Handling

import {
  SecureChatCompletion,
  AuthenticationError,
  RateLimitError,
  ForbiddenError,
  InvalidRequestError,
  ServerError,
  ServiceUnavailableError,
  APIConnectionError,
  SecurityError,
} from 'nomyo-js';

const client = new SecureChatCompletion({ apiKey: process.env.NOMYO_API_KEY });

try {
  const response = await client.create({
    model: 'Qwen/Qwen3-0.6B',
    messages: [{ role: 'user', content: 'Hello' }],
  });
  console.log(response.choices[0].message.content);

} catch (err) {
  if (err instanceof AuthenticationError) {
    console.error('Check your API key:', err.message);
  } else if (err instanceof RateLimitError) {
    console.error('Rate limit hit after all retries:', err.message);
  } else if (err instanceof ForbiddenError) {
    console.error('Model not allowed for this security tier:', err.message);
  } else if (err instanceof InvalidRequestError) {
    console.error('Bad request:', err.message, err.errorDetails);
  } else if (err instanceof ServerError || err instanceof ServiceUnavailableError) {
    console.error('Server error after retries:', err.message);
  } else if (err instanceof APIConnectionError) {
    console.error('Network error after retries:', err.message);
  } else if (err instanceof SecurityError) {
    console.error('Security/crypto failure:', err.message);
  } else {
    throw err;
  }
}

Real-World Scenarios

Chat Application with History

import { SecureChatCompletion } from 'nomyo-js';

class SecureChatApp {
  constructor(apiKey) {
    this.client = new SecureChatCompletion({ apiKey });
    this.history = [];
  }

  async chat(userMessage) {
    this.history.push({ role: 'user', content: userMessage });

    const response = await this.client.create({
      model: 'Qwen/Qwen3-0.6B',
      messages: this.history,
      temperature: 0.7,
    });

    const assistantMessage = response.choices[0].message;
    this.history.push({ role: assistantMessage.role, content: assistantMessage.content });
    return assistantMessage.content;
  }

  dispose() {
    this.client.dispose();
  }
}

const app = new SecureChatApp(process.env.NOMYO_API_KEY);

const r1 = await app.chat("What's your name?");
console.log('Assistant:', r1);

const r2 = await app.chat('What did I just ask you?');
console.log('Assistant:', r2);

app.dispose();

Per-Request Base URL Override

For multi-tenant setups or testing against different router instances from a single client:

const client = new SecureChatCompletion({
  baseUrl: 'https://primary.nomyo.ai:12435',
  apiKey: process.env.NOMYO_API_KEY,
});

// This single request goes to a different router; a temporary client is
// created, used, and disposed automatically — the main client is unchanged
const response = await client.create({
  model: 'Qwen/Qwen3-0.6B',
  messages: [{ role: 'user', content: 'Hello from secondary router' }],
  base_url: 'https://secondary.nomyo.ai:12435',
});

Environment-Based Configuration

import 'dotenv/config';
import { SecureChatCompletion } from 'nomyo-js';

const client = new SecureChatCompletion({
  baseUrl:    process.env.NOMYO_SERVER_URL ?? 'https://api.nomyo.ai',
  apiKey:     process.env.NOMYO_API_KEY,
  keyDir:     process.env.NOMYO_KEY_DIR   ?? 'client_keys',
  maxRetries: Number(process.env.NOMYO_MAX_RETRIES ?? '2'),
  debug:      process.env.NODE_ENV === 'development',
});

Batch Processing

Sequential (Rate-Limit-Safe)

const queries = [
  'Summarise document A',
  'Summarise document B',
  'Summarise document C',
];

const summaries = [];
for (const query of queries) {
  const response = await client.create({
    model: 'Qwen/Qwen3-0.6B',
    messages: [{ role: 'user', content: query }],
  });
  summaries.push(response.choices[0].message.content);
  // Optional: add a small delay to stay within rate limits
  await new Promise(r => setTimeout(r, 600));
}

Concurrent (With Throttling)

// Process in batches of 2 (the default rate limit)
async function batchN(items, batchSize, fn) {
  const results = [];
  for (let i = 0; i < items.length; i += batchSize) {
    const batch = items.slice(i, i + batchSize);
    const batchResults = await Promise.all(batch.map(fn));
    results.push(...batchResults);
    if (i + batchSize < items.length) {
      await new Promise(r => setTimeout(r, 1100)); // wait >1 s between batches
    }
  }
  return results;
}

const summaries = await batchN(documents, 2, async (doc) => {
  const response = await client.create({
    model: 'Qwen/Qwen3-0.6B',
    messages: [{ role: 'user', content: `Summarise: ${doc}` }],
  });
  return response.choices[0].message.content;
});

Thinking Models

const response = await client.create({
  model: 'LiquidAI/LFM2.5-1.2B-Thinking',
  messages: [{ role: 'user', content: 'Is 9.9 larger than 9.11?' }],
});

const { content, reasoning_content } = response.choices[0].message;
console.log('Reasoning:', reasoning_content);   // internal chain-of-thought
console.log('Answer:',    content);             // final answer to the user

Browser Usage

<!DOCTYPE html>
<html>
<head>
  <title>NOMYO Secure Chat</title>
</head>
<body>
  <textarea id="input" placeholder="Ask something..."></textarea>
  <button id="send">Send</button>
  <div id="output"></div>

  <script type="module">
    import { SecureChatCompletion } from 'https://unpkg.com/nomyo-js/dist/browser/index.mjs';

    // In production, proxy through your backend instead of exposing the API key
    const client = new SecureChatCompletion({
      baseUrl: 'https://api.nomyo.ai',
      apiKey:  'your-api-key',        // see note above
    });

    document.getElementById('send').addEventListener('click', async () => {
      const text = document.getElementById('input').value.trim();
      if (!text) return;

      document.getElementById('output').textContent = 'Thinking...';
      try {
        const response = await client.create({
          model: 'Qwen/Qwen3-0.6B',
          messages: [{ role: 'user', content: text }],
        });
        document.getElementById('output').textContent =
          response.choices[0].message.content;
      } catch (err) {
        document.getElementById('output').textContent = `Error: ${err.message}`;
      }
    });
  </script>
</body>
</html>

Advanced Key Management

Custom Key Directory

const client = new SecureChatCompletion({
  apiKey:  process.env.NOMYO_API_KEY,
  keyDir:  '/var/lib/myapp/nomyo-keys',   // outside project directory
  keyRotationDir:      '/var/lib/myapp/nomyo-keys',
  keyRotationPassword: process.env.NOMYO_KEY_PASSWORD,
});

Generating Keys Manually

import { SecureCompletionClient } from 'nomyo-js';

const client = new SecureCompletionClient({
  routerUrl: 'https://api.nomyo.ai',
});

// Generate a new 4096-bit key pair and save it with password protection
await client.generateKeys({
  saveToFile: true,
  keyDir:     'client_keys',
  password:   process.env.NOMYO_KEY_PASSWORD,
});

console.log('Keys generated and saved to client_keys/');
client.dispose();

Loading Keys Explicitly

import { SecureCompletionClient } from 'nomyo-js';

const client = new SecureCompletionClient({ routerUrl: 'https://api.nomyo.ai' });

await client.loadKeys(
  'client_keys/private_key.pem',
  'client_keys/public_key.pem',
  process.env.NOMYO_KEY_PASSWORD
);

// Now send requests using the loaded keys
const result = await client.sendSecureRequest(
  { model: 'Qwen/Qwen3-0.6B', messages: [{ role: 'user', content: 'Hello' }] },
  crypto.randomUUID()
);
client.dispose();

Inspecting Memory Protection

import { getMemoryProtectionInfo } from 'nomyo-js';

const info = getMemoryProtectionInfo();

console.log(`Memory method: ${info.method}`);        // 'zero-only' or 'mlock'
console.log(`Can lock:      ${info.canLock}`);       // true if native addon present
console.log(`Details:       ${info.details}`);

TypeScript

Full type safety out of the box:

import {
  SecureChatCompletion,
  ChatCompletionRequest,
  ChatCompletionResponse,
  Message,
} from 'nomyo-js';

const client = new SecureChatCompletion({ apiKey: process.env.NOMYO_API_KEY! });

const messages: Message[] = [
  { role: 'user', content: 'Hello!' },
];

const request: ChatCompletionRequest = {
  model: 'Qwen/Qwen3-0.6B',
  messages,
  temperature: 0.7,
};

const response: ChatCompletionResponse = await client.create(request);
const content = response.choices[0].message.content;

client.dispose();