{"id":19245080,"url":"https://github.com/rxtoolkit/genai","last_synced_at":"2026-02-25T18:07:20.621Z","repository":{"id":252253172,"uuid":"834244299","full_name":"rxtoolkit/genai","owner":"rxtoolkit","description":"Generative AI tools for RxJS (e.g. OpenAI, Anthropic, Cohere, HuggingFace, etc)","archived":false,"fork":false,"pushed_at":"2024-10-24T01:25:37.000Z","size":387,"stargazers_count":3,"open_issues_count":4,"forks_count":0,"subscribers_count":1,"default_branch":"master","last_synced_at":"2025-10-06T00:32:49.029Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"JavaScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/rxtoolkit.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":null,"license":"LICENSE.md","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null}},"created_at":"2024-07-26T18:38:48.000Z","updated_at":"2025-01-30T12:11:32.000Z","dependencies_parsed_at":"2024-08-08T17:03:38.412Z","dependency_job_id":"585d5994-8a6b-4caf-976f-f1b5495a7105","html_url":"https://github.com/rxtoolkit/genai","commit_stats":null,"previous_names":["rxtoolkit/genai"],"tags_count":1,"template":false,"template_full_name":null,"purl":"pkg:github/rxtoolkit/genai","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/rxtoolkit%2Fgenai","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/rxtoolkit%2Fgenai/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/rxtoolkit%2Fgenai/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/rxtoolkit%2Fgenai/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/rxtoolkit","download_url":"https://codeload.github.com/rxtoolkit/genai/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/rxtoolkit%2Fgenai/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":29833837,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-02-25T17:57:15.019Z","status":"ssl_error","status_checked_at":"2026-02-25T17:56:11.472Z","response_time":61,"last_error":"SSL_read: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2024-11-09T17:26:34.929Z","updated_at":"2026-02-25T18:07:20.589Z","avatar_url":"https://github.com/rxtoolkit.png","language":"JavaScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# @rxtk/genai\n\u003e ⚡️ Generative AI toolkit for RxJS \n\nThis package is inspired by Python's [Langchain](https://www.langchain.com) toolkit. It provides convenient operators for composing generative AI pipelines in a way that is:\n- **Opinionated**: Pipelines use sensible defaults for common use cases to avoid repititious code.\n- **Flexible**: Advanced configuration options and LLM controls are exposed for use cases that require them.\n- **Concise**: Syntax allows pipelines to be composed with as few lines of code as possible.\n- **Functional**: Rather than using complex class-based abstractions like Langchain, this package uses RxJS to provide underlying FP abstractions.\n- **Vendor Agnostic**: The toolkit works with various GenAI vendors (OpenAI, Anthropic, etc) and normalizes the data. New vendors or custom services can be easily integrated! (Example provided below.)\n- **DRY**: Stages of the pipeline can be easily abstracted away and reused.\n- **Readable**: FP pipes and functional composition make pipelines easy to read and reason about quickly.\n- **Decoupled**: Stages of a pipeline can be easily abstracted into one or more independent modules.\n\n## Installation\n```bash\nnpm i @rxtk/genai\n```\n\n```bash\nyarn add @rxtk/genai\n```\n\n## Quick Examples\n\u003e 🔒 **Authentication**: These examples assume you already have an API key for each vendor and that it is stored in the conventional environment variable (e.g. OPENAI_API_KEY). You can also provide a key by passing `options.apiKey` to the `toModel` operator.\n\n### Simple GenAI Pipeline\n```js\nimport {of} from 'rxjs';\nimport {map} from 'rxjs/operators';\nimport {toPrompt,toModel,toCompletionString} from '@rxtk/genai';\n\nconst input = [\n  {language: 'german', phrase: 'hello'},\n  {language: 'french', phrase: 'goodbye'},\n  {language: 'pirate', phrase: 'yes, my friend'},\n  {language: 'doublespeak', phrase: 'They are saying rebellious things.'},\n];\nconst completionString$ = of(...input).pipe(\n  // inject variables into a prompt template\n  toPrompt('Translate the phrase into the language: {{language}}.\\nPhrase: {{phrase}}.\\nTranslation: '),\n  // send the prompt to the desired vendor and model\n  toModel({vendor: 'openai', model: 'gpt-4o'}),\n  // retrieve the string value of the completion\n  toCompletionString()\n);\ncompletionString$.subscribe(console.log);\n// Hallo\n// au revoir\n// Yarr matey\n// They are saying quack speak\n```\n\n### With Templates for Multiple Roles\n```js\nimport {of} from 'rxjs';\nimport {map} from 'rxjs/operators';\nimport {toPrompt,toModel,toCompletionString} from '@rxtk/genai';\n\nconst input = [\n  {language: 'german', phrase: 'hello'},\n  {language: 'french', phrase: 'goodbye'},\n];\nconst completionString$ = of(...input).pipe(\n  // inject variables into a prompt template\n  toPrompt([\n    ['system', 'Translate the phrase into the language: {{language}}'],\n    ['user', '{{phrase}}'],\n  ]),\n  // send the prompt to the desired vendor and model\n  toModel({vendor: 'openai', model: 'gpt-4o'}),\n  // retrieve the string value of the completion\n  toCompletionString()\n);\ncompletionString$.subscribe(console.log);\n// Hallo\n// au revoir\n```\n\n### Generate Completions from Multiple Vendors\n```js\nimport {concat,of} from 'rxjs';\nimport {map} from 'rxjs/operators';\nimport {toPrompt,toModel,toCompletionString} from '@rxtk/genai';\n\nconst pipelines = [\n  {\n    vendor: 'openai',\n    model: 'gpt-4o',\n  },\n  {\n    vendor: 'anthropic',\n    model: 'claude-3-opus-20240229',\n  },\n  {\n    vendor: 'cohere',\n    model: 'r-plus',\n  },\n];\n\nconst input = [\n  {language: 'german', phrase: 'hello'},\n  {language: 'french', phrase: 'goodbye'}\n];\nconst input$ = of(...input);\n\nconst workflows = pipelines.map(p =\u003e \n  input$.pipe(\n    toPrompt([\n      ['system', 'Translate the phrase into the language: {{language}}'],\n      ['user', '{{phrase}}'],\n    ]),\n    toModel(p),\n    toCompletionString(),\n    map(c =\u003e `vendor=${p.vendor}, model=${p.model} completion='${c}'`)\n  )\n);\n\nconst output$ = concat(...workflows);\noutput$.subscribe(console.log);\n// vendor=openai, model=gpt-40, completion='Hallo'\n// ...\n// vendor=anthropic, model=claude-3-opus-20240229, completion='Hallo'\n// ...\n```\n\n### (Beta): Generate Completions from a Custom Model\n```js\n// If you want to use an LLM service that is not supported, you can write your own:\nimport axios from 'axios';\nimport {map,mergeMap} from 'rxjs/operators';\n\n// This is just an RxJS operator\nconst toMyService = (params, options = {}) =\u003e source$ =\u003e source$.pipe(\n  mergeMap(messages =\u003e {\n    const {model, apiKey} = params;\n    const data$ = from(\n      axios({\n        method: 'post',\n        url: `https://api.myfancyllm.com/v1/messages/${model}`,\n        data: { \n          model: model || 'default-model',\n          messages,\n          ...options\n        },\n        headers: {\n          'Authorization': `Bearer ${options?.apiKey || process.env?.CUSTOM_LLM_API_KEY}`,\n        }\n      })\n    );\n    return data$.pipe(\n      map(\n        options?.normalize \n        // write a custom parser to normalize the response. \n        // for examples see ./src/internals/toOpenAI.js, ./src/internals/toAnthropic.js, etc.\n        ? response =\u003e response.data \n        : x =\u003e x\n      )\n    );\n  }),\n);\n\nconst input = [\n  {language: 'german', phrase: 'hello'},\n  {language: 'french', phrase: 'goodbye'}\n];\nconst completionString$ = of(...input).pipe(\n  // inject variables into a prompt template\n  toPrompt([\n    ['system', 'Translate the phrase into the language: {{language}}'],\n    ['user', '{{phrase}}'],\n  ]),\n  // send the prompt to your custom integration\n  toModel({customOperator: toMyService}),\n  // retrieve the string value of the completion\n  toCompletionString()\n);\ncompletionString$.subscribe(console.log);\n// Hallo\n// au revoir\n```\n\n## API\n### `toModel({vendor='openai', model='gpt-4o'}, options)([{role\u003cString\u003e,content\u003cString\u003e}])`\n- `vendor!\u003cString\u003e`: Like `openai`, `anthropic`, `cohere`, or `huggingface`. Check `./lib/toModel.js` for the complete, up-to-date list.\n- `model`: The vendor's name for the model being used. Example: `gpt-3.5-turbo`.\n- `options.apiKey`: API key for the vendor. If not provided, the operator will attempt to find the environment variable for the vendor using the default name for the variable.\n- `options.normalize`(default=`true`): whether to normalize responses or return raw responses.\n-  `options.llm`: Configuration options to pass directly to the model (like `temperature`, etc).\n-  `options.batchSize`: If provided, requests to the LLM will be batched and returned as an array of completions instead of individual completions.\n```js\nimport {from} from 'rxjs';\nimport {map} from 'rxjs/operators';\nimport {toModel} from '@rxtk/genai';\n\n// Note on API keys: Each model will look for the default environment variable for each vendor API key and other settings. Those can also be passed in using the apiKey configuration variable\nconst string$ = from(['hello', 'goodbye']);\nconst output$ = string$.pipe(\n  map(str =\u003e [\n    {role: 'system', content: 'Translate the input text into German.'},\n    {role: 'user', content: str}\n  ]),\n  // accepts an array of messages\n  toModel({vendor: 'openai', model: 'gpt-4o'})\n);\noutput$.subscribe(console.log);\n// Outputs the normalized response data from each api call\n```\n\n### `toPrompt(params)(dictionary{})`\n- `params[[role\u003cString\u003e, template\u003cString\u003e]]`: can accept an array of messages with the shape [role\u003cString\u003e, template\u003cString\u003e] like `toPrompt([ ['user', 'Hello {{name}}'], ['system', 'Answer nicely but be cool and edgy.']])({name: 'Woody'})`\n- `params\u003cString\u003e`: For simple use cases, roles do not need to be specified. If a string is passed, then it will simply be fed in as the user role. Example: `toPrompt(\"Hello {{name}}.\")({name: 'Buzz'})`.\n- `dictionary{}`: A set of key value pairs to interpolate into the template.\n\nThe content can wrap variable names in curly braces like `'Insert a variable here: {{myVar}}'`. If the object passed into the operator at each iteration contains any of those those keys, the template will inject the value of each key into the string.\n```js\nimport {from} from 'rxjs';\nimport {toModel, toPrompt} from '@rxtk/genai';\n\nconst promptTemplate = [\n  ['system', 'Translate the phrase into the language: {{language}}'],\n  ['user', '{{phrase}}'],\n];\nconst inputString$ = from([\n  {language: 'german', phrase: 'hello'}, \n  {language: 'french', phrase: 'goodbye'},\n]);\nconst output$ = string$.pipe(\n  toPrompt(promptTemplate),\n  toModel({vendor: 'openai', model: 'gpt-4o'})\n);\noutput$.subscribe(console.log); \n// Output:\n// foo\n// bar\n```\n\n### `toCompletionString()(rawResponse)`\nOutputs the completion string from a completion object.\n```js\nimport {from} from 'rxjs';\nimport {toModel, toCompletionString} from '@rxtk/genai';\n\nconst prompt$ = from([\n  {role: 'user', content: 'What is the capital of Michigan?'},\n  {role: 'user', content: 'What is the capital of Ohio?'},\n]);\nconst output$ = prompt$.pipe(\n  toModel({vendor: 'anthropic'}),\n  toCompletionString()\n);\noutput$.subscribe(console.log);\n// Output:\n// The capital of Michigan is Lansing.\n// Who really cares what the capital of Ohio is? Just kidding. That was mean.\n```\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Frxtoolkit%2Fgenai","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Frxtoolkit%2Fgenai","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Frxtoolkit%2Fgenai/lists"}