Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
521 changes: 520 additions & 1 deletion package-lock.json

Large diffs are not rendered by default.

112 changes: 0 additions & 112 deletions skills/ai-tools/scripts/embeddings.cjs

This file was deleted.

18 changes: 11 additions & 7 deletions skills/ai-tools/scripts/embeddings.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,19 +8,18 @@
const fs = require('fs');
const path = require('path');

const args = process.argv.slice(2);

/**
* Parse command-line arguments into an options object for embedding generation.
*
* Recognizes `--model`, `--dimensions`, and `--output`; the first non-option argument is used as the input text.
* @param {string[]} args - CLI arguments
* @returns {{text: string, model: string, dimensions: number, output: string|null}} An object containing:
* - `text`: the input text to embed (empty string if not provided),
* - `model`: embedding model name (default `"text-embedding-3-small"`),
* - `dimensions`: requested embedding dimensionality (default `1536`),
* - `output`: optional file path to save the result, or `null` if not set.
*/
function parseArgs() {
function parseArgs(args) {
const result = {
text: '',
model: 'text-embedding-3-small',
Expand Down Expand Up @@ -92,12 +91,13 @@ async function generateEmbeddings(text, model, dimensions) {
}

/**
* Parse command-line arguments, generate embeddings for the provided text, and either save the full result to a file or print a truncated preview.
* Orchestrates the CLI flow: parse arguments, generate embeddings for the provided text, and output or save the results.
*
* Parses argv for text, model, dimensions, and output options; if no text is provided prints usage and exits with code 1. Calls generateEmbeddings with the parsed options, and on success either writes the complete result as pretty JSON to the specified output file or prints a truncated embedding preview (first five values plus an indicator of total length). On failure logs a JSON error and exits with code 1.
* If no text is provided, prints usage information and exits with code 1. On success, writes the full result to the specified output file when an output path is given; otherwise prints a truncated embedding preview. On error, prints a JSON object containing the error message and exits with code 1.
*/
async function main() {
const options = parseArgs();
const args = process.argv.slice(2);
const options = parseArgs(args);

if (!options.text) {
console.error('Usage: node embeddings.js <text> [OPTIONS]');
Expand Down Expand Up @@ -134,4 +134,8 @@ async function main() {
}
}

main();
if (require.main === module) {
main();
}

module.exports = { generateEmbeddings, main };
111 changes: 0 additions & 111 deletions skills/ai-tools/scripts/embeddings.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -501,115 +501,4 @@ describe('embeddings.js', () => {
expect(output.embedding[1]).toBe(-0.987654321);
expect(output.embedding[2]).toBe(0.000000001);
});

it('handles concurrent embedding generation', async () => {
const mockResponse = {
ok: true,
json: async () => ({
data: [{ embedding: [0.1, 0.2, 0.3] }],
usage: { total_tokens: 10 },
}),
};

mockFetch.mockResolvedValue(mockResponse);

const result1Promise = runScript(['text one'], {
OPENAI_API_KEY: 'test-key',
});
const result2Promise = runScript(['text two'], {
OPENAI_API_KEY: 'test-key',
});

const [result1, result2] = await Promise.all([result1Promise, result2Promise]);

expect(result1.code).toBe(0);
expect(result2.code).toBe(0);
});

it('handles API server returning 500 internal error', async () => {
const mockResponse = {
ok: false,
status: 500,
text: async () => 'Internal server error',
};

mockFetch.mockResolvedValue(mockResponse);

const result = await runScript(['test text'], {
OPENAI_API_KEY: 'test-key',
});

expect(result.code).toBe(1);
const error = JSON.parse(result.stderr);
expect(error.error).toContain('500');
});

it('handles embeddings for text with only special characters', async () => {
const mockResponse = {
ok: true,
json: async () => ({
data: [{ embedding: [0.5, 0.6] }],
usage: { total_tokens: 3 },
}),
};

mockFetch.mockResolvedValue(mockResponse);

const result = await runScript(['!@#$%^&*()_+-={}[]|:;"<>,.?/'], {
OPENAI_API_KEY: 'test-key',
});

expect(result.code).toBe(0);
const output = JSON.parse(result.stdout);
expect(output.embedding).toBeDefined();
});

it('handles output file path with special characters', async () => {
const fs = require('fs');
const path = require('path');
const tempFile = path.join('/tmp', `test embeddings [special] (chars) ${Date.now()}.json`);

const mockResponse = {
ok: true,
json: async () => ({
data: [{ embedding: [0.1, 0.2] }],
usage: { total_tokens: 5 },
}),
};

mockFetch.mockResolvedValue(mockResponse);

try {
const result = await runScript(['text', '--output', tempFile], {
OPENAI_API_KEY: 'test-key',
});

expect(result.code).toBe(0);
expect(fs.existsSync(tempFile)).toBe(true);
} finally {
if (fs.existsSync(tempFile)) {
fs.unlinkSync(tempFile);
}
}
});

it('handles negative dimensions parameter', async () => {
const mockResponse = {
ok: true,
json: async () => ({
data: [{ embedding: [] }],
usage: { total_tokens: 5 },
}),
};

mockFetch.mockResolvedValue(mockResponse);

const result = await runScript(['text', '--dimensions', '-100'], {
OPENAI_API_KEY: 'test-key',
});

expect(result.code).toBe(0);
const output = JSON.parse(result.stdout);
expect(output.dimensions).toBe(-100);
});
});
Loading
Loading