{"id":15432954,"url":"https://github.com/simonw/llm-openrouter","last_synced_at":"2026-04-20T20:00:38.061Z","repository":{"id":189442516,"uuid":"680664747","full_name":"simonw/llm-openrouter","owner":"simonw","description":"LLM plugin for models hosted by OpenRouter","archived":false,"fork":false,"pushed_at":"2026-04-05T15:38:47.000Z","size":261,"stargazers_count":307,"open_issues_count":15,"forks_count":37,"subscribers_count":9,"default_branch":"main","last_synced_at":"2026-04-17T13:03:11.140Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":"","language":"Python","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"apache-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/simonw.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","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},"funding":{"github":"simonw"}},"created_at":"2023-08-20T01:50:26.000Z","updated_at":"2026-04-09T19:36:43.000Z","dependencies_parsed_at":"2025-08-20T18:33:39.656Z","dependency_job_id":"88e72bab-101a-4155-af31-c0a5a3ab9208","html_url":"https://github.com/simonw/llm-openrouter","commit_stats":{"total_commits":11,"total_committers":3,"mean_commits":"3.6666666666666665","dds":"0.18181818181818177","last_synced_commit":"b01ab7150846c251ac923646c8917b02cd5a5199"},"previous_names":["simonw/llm-openrouter"],"tags_count":6,"template":false,"template_full_name":null,"purl":"pkg:github/simonw/llm-openrouter","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/simonw%2Fllm-openrouter","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/simonw%2Fllm-openrouter/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/simonw%2Fllm-openrouter/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/simonw%2Fllm-openrouter/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/simonw","download_url":"https://codeload.github.com/simonw/llm-openrouter/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/simonw%2Fllm-openrouter/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":32063458,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-04-20T11:35:06.609Z","status":"ssl_error","status_checked_at":"2026-04-20T11:34:48.899Z","response_time":94,"last_error":"SSL_connect returned=1 errno=0 peeraddr=140.82.121.6:443 state=error: 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-10-01T18:29:42.615Z","updated_at":"2026-04-20T20:00:38.055Z","avatar_url":"https://github.com/simonw.png","language":"Python","funding_links":["https://github.com/sponsors/simonw"],"categories":["Python"],"sub_categories":[],"readme":"# llm-openrouter\n\n[![PyPI](https://img.shields.io/pypi/v/llm-openrouter.svg)](https://pypi.org/project/llm-openrouter/)\n[![Changelog](https://img.shields.io/github/v/release/simonw/llm-openrouter?include_prereleases\u0026label=changelog)](https://github.com/simonw/llm-openrouter/releases)\n[![Tests](https://github.com/simonw/llm-openrouter/workflows/Test/badge.svg)](https://github.com/simonw/llm-openrouter/actions?query=workflow%3ATest)\n[![License](https://img.shields.io/badge/license-Apache%202.0-blue.svg)](https://github.com/simonw/llm-openrouter/blob/main/LICENSE)\n\n[LLM](https://llm.datasette.io/) plugin for models hosted by [OpenRouter](https://openrouter.ai/)\n\n## Installation\n\nFirst, [install the LLM command-line utility](https://llm.datasette.io/en/stable/setup.html).\n\nNow install this plugin in the same environment as LLM.\n```bash\nllm install llm-openrouter\n```\n\n## Configuration\n\nYou will need an API key from OpenRouter. You can [obtain one here](https://openrouter.ai/keys).\n\nYou can set that as an environment variable called `OPENROUTER_KEY`, or add it to the `llm` set of saved keys using:\n\n```bash\nllm keys set openrouter\n```\n```\nEnter key: \u003cpaste key here\u003e\n```\n\n## Usage\n\nTo list available models, run:\n```bash\nllm models list\n```\nYou should see a list that looks something like this:\n```\nOpenRouter: openrouter/openai/gpt-3.5-turbo\nOpenRouter: openrouter/anthropic/claude-sonnet-4\nOpenRouter: openrouter/meta-llama/llama-2-70b-chat\n...\n```\nThe list of models from OpenRouter is cached for an hour. You can force a refresh using this command:\n```bash\nllm openrouter refresh\n```\n\nTo run a prompt against a model, pass its full model ID to the `-m` option, like this:\n```bash\nllm -m openrouter/anthropic/claude-sonnet-4 \"Five spooky names for a pet tarantula\"\n```\nYou can set a shorter alias for a model using the `llm aliases` command like so:\n```bash\nllm aliases set claude openrouter/anthropic/claude-sonnet-4\n```\nNow you can prompt Claude using:\n```bash\ncat llm_openrouter.py | llm -m claude -s 'write some pytest tests for this'\n```\n\nImages are supported too, for some models:\n```bash\nllm -m openrouter/anthropic/claude-3.5-sonnet 'describe this image' -a https://static.simonwillison.net/static/2024/pelicans.jpg\nllm -m openrouter/anthropic/claude-3-haiku 'extract text' -a page.png\n```\n\n### Vision models\n\nSome OpenRouter models can accept image attachments. Run this command:\n\n```bash\nllm models --options -q openrouter\n```\nAnd look for models that list these attachment types:\n\n```\n  Attachment types:\n    application/pdf, image/gif, image/jpeg, image/png, image/webp\n```\nYou can feed these models images as URLs or file paths, for example:\n\n```bash\nllm -m openrouter/google/gemini-flash-1.5 'describe image' \\\n  -a https://static.simonwillison.net/static/2025/two-pelicans.jpg\n```\n\n### Schemas\n\nLLM includes support for [schemas](https://llm.datasette.io/en/stable/schemas.html), allowing you to control the JSON structure of the output returned by the model.\n\nSome of the models provided by OpenRouter are compatible with this feature, see [their full list of structured output models](https://openrouter.ai/models?order=newest\u0026supported_parameters=structured_outputs) for details.\n\n`llm-openrouter` currently enables schema support for the models in that list. Models have varying levels of quality in their schema support, so test carefully rather than assuming all models will correctly work the same.\n\n```bash\nllm -m openrouter/google/gemini-flash-1.5 'invent 3 cool capybaras' \\\n  --schema-multi 'name,bio'\n```\nOutput:\n```json\n{\n  \"items\": [\n    {\n      \"bio\": \"Chill vibes only.  Spends most days floating on lily pads, occasionally accepting head scratches from passing frogs.\",\n      \"name\": \"Professor Fluffernutter\"\n    },\n    {\n      \"bio\": \"A thrill-seeker!  Capybara extraordinaire known for her daring escapes from the local zoo and impromptu skateboarding sessions.\",\n      \"name\": \"Capybara-bara the Bold\"\n    },\n    {\n      \"bio\": \"A renowned artist, creating masterpieces using mud, leaves, and her own surprisingly dexterous paws.\",\n      \"name\": \"Michelangelo Capybara\"\n    }\n  ]\n}\n```\n\n### Tools\n\nMost OpenRouter models support [tool calls](https://llm.datasette.io/en/stable/tools.html). You can try that out like so:\n\n```bash\nllm -m openrouter/openai/gpt-5 \\\n  -T llm_version -T llm_time \\\n  \"What version of LLM and what time is it?\" \\\n  --tools-debug\n```\nExample output:\n```\nTool call: llm_version({})\n  0.27.1\n\n\nTool call: llm_time({})\n  {\n    \"utc_time\": \"2025-09-20 23:35:53 UTC\",\n    \"utc_time_iso\": \"2025-09-20T23:35:53.205247+00:00\",\n    \"local_timezone\": \"PDT\",\n    \"local_time\": \"2025-09-20 16:35:53\",\n    \"timezone_offset\": \"UTC-7:00\",\n    \"is_dst\": true\n  }\n\nLLM version: 0.27.1\nCurrent time: 2025-09-20 16:35:53 PDT (2025-09-20 23:35:53 UTC)\n```\n\n### Reasoning\n\nSome OpenRouter models such as [GPT-5](https://openrouter.ai/openai/gpt-5) support options for controlling reasoning:\n\n- `-o reasoning_effort low|medium|high` - control reasoning effort\n- `-o reasoning_max_tokens 2048` - an alternative way of specifying effort for some models\n- `-o reasoning_enabled true` - use this to enable reasoning without setting an effort via one of the other two options\n\nFor example:\n\n```bash\nllm -m openrouter/openai/gpt-5 \\\n   'prove dogs exist' \\\n   -o reasoning_effort high\n```\n\n### Provider routing\n\nOpenRouter offers [comprehensive options](https://openrouter.ai/docs/features/provider-routing) for controlling which underlying provider your request is routed to.\n\nYou can specify these using the OpenRouter JSON format, then pass that to LLM using the `-o provider '{JSON goes here}` option:\n\n```bash\nllm -m openrouter/meta-llama/llama-3.1-8b-instruct hi \\\n  -o provider '{\"quantizations\": [\"fp8\"]}'\n```\nThis specifies that you would like only providers that [support fp8 quantization](https://openrouter.ai/docs/features/provider-routing#example-requesting-fp8-quantization) for that model.\n\n### Incorporating search results from Exa\n\nOpenRouter have [a partnership](https://openrouter.ai/docs/features/web-search) with [Exa](https://exa.ai/) where prompts through _any_ supported model can be augmented with relevant search results from the Exa index - a form of RAG.\n\nEnable this feature using the `-o online 1` option:\n\n```bash\nllm -m openrouter/mistralai/mistral-small -o online 1 'key events on march 1st 2025'\n```\nConsult the OpenRouter documentation for [current pricing](https://openrouter.ai/docs/features/web-search#pricing).\n\n### Listing models\n\nThe `llm models -q openrouter` command will display all available models, or you can use this command to see more detailed JSON:\n\n```bash\nllm openrouter models\n```\nOutput starts like this:\n```yaml\n- id: latitudegames/wayfarer-large-70b-llama-3.3\n  name: LatitueGames: Wayfarer Large 70B Llama 3.3\n  context_length: 128,000\n  architecture: text-\u003etext Llama3\n  pricing: prompt $0.7/M, completion $0.7/M\n\n- id: thedrummer/skyfall-36b-v2\n  name: TheDrummer: Skyfall 36B V2\n  context_length: 64,000\n  architecture: text-\u003etext Other\n  pricing: prompt $0.5/M, completion $0.5/M\n\n- id: microsoft/phi-4-multimodal-instruct\n  name: Microsoft: Phi 4 Multimodal Instruct\n  context_length: 131,072\n  architecture: text+image-\u003etext Other\n  pricing: prompt $0.07/M, completion $0.14/M, image $0.2476/K\n```\nAdd `--json` to get back JSON instead, which looks like this:\n```json\n[\n  {\n    \"id\": \"microsoft/phi-4-multimodal-instruct\",\n    \"name\": \"Microsoft: Phi 4 Multimodal Instruct\",\n    \"created\": 1741396284,\n    \"description\": \"Phi-4 Multimodal Instruct is a versatile...\",\n    \"context_length\": 131072,\n    \"architecture\": {\n      \"modality\": \"text+image-\u003etext\",\n      \"tokenizer\": \"Other\",\n      \"instruct_type\": null\n    },\n    \"pricing\": {\n      \"prompt\": \"0.00000007\",\n      \"completion\": \"0.00000014\",\n      \"image\": \"0.0002476\",\n      \"request\": \"0\",\n      \"input_cache_read\": \"0\",\n      \"input_cache_write\": \"0\",\n      \"web_search\": \"0\",\n      \"internal_reasoning\": \"0\"\n    },\n    \"top_provider\": {\n      \"context_length\": 131072,\n      \"max_completion_tokens\": null,\n      \"is_moderated\": false\n    },\n    \"per_request_limits\": null\n  }\n```\nAdd `--free` for a list of just the models that are [available for free](https://openrouter.ai/models?max_price=0).\n```bash\nllm openrouter models --free\n```\n\n### Information about your API key\n\nThe `llm openrouter key` command shows you information about your current API key, including rate limits:\n\n```bash\nllm openrouter key\n```\nExample output:\n```json\n{\n  \"label\": \"sk-or-v1-0fa...240\",\n  \"limit\": null,\n  \"usage\": 0.65017511,\n  \"limit_remaining\": null,\n  \"is_free_tier\": false,\n  \"rate_limit\": {\n    \"requests\": 40,\n    \"interval\": \"10s\"\n  }\n}\n```\nThis will default to inspecting the key you have set using `llm keys set openrouter` or using the `OPENROUTER_KEY` environment variable.\n\nYou can inspect a different key by passing the key itself - or the name of the key in the `llm keys` list - as the `--key` option:\n\n```bash\nllm openrouter key --key sk-xxx\n```\n\n## Development\n\nTo set up this plugin locally, first checkout the code. Then run the tests with `uv`:\n```bash\ncd llm-openrouter\nuv run pytest\n```\nTo run LLM with the plugin available:\n```bash\nuv run llm models\n```\nTo update recordings and snapshots, run:\n```bash\nPYTEST_OPENROUTER_KEY=\"$(llm keys get openrouter)\" \\\n  uv run pytest --record-mode=rewrite --inline-snapshot=fix\n```\n\nIf tests against additional models are added, update `tests/models_persister.py` to preserve those model ids in the recordings.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fsimonw%2Fllm-openrouter","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fsimonw%2Fllm-openrouter","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fsimonw%2Fllm-openrouter/lists"}