**190proof opinionated unified interface to interact with multiple AI providers*
原帖
**190proof opinionated unified interface to interact with multiple AI providers**
> LLM router with aggressive normalization and stubborn error-handling. - 0xmmo/190proof 190proof An opinionated unified interface for interacting with multiple AI providers including OpenAI , Anthropic , Google , Groq , OpenRouter , and AWS Bedrock . This package provides a consistent API for making requests to different LLM providers while handling retries, streaming, and multimodal inputs. Features Fully-local unified interface across multiple AI providers that includes: 🛠️ Consistent function/tool calling across all providers 💬 Consistent message alternation & system instructions 🖼️ Image format & size normalization 🔄 Automatic retries with configurable attempts 📡 Streaming by default ☁️ Cloud service providers supported (Azure, AWS Bedrock) 🔌 Provider prefix strings for any model without waiting for package updates Installation npm install 190proof Usage Basic Example Use any model from any provider with the provider:model-id format: import { callWithRetries , GenericPayload } from "190proof" ; const payload : GenericPayload = { model : "openai:gpt-4o-mini" , messages : \[ { role : "user" , content : "Tell me a joke." , } , \] , } ; const response = await callWithRetries ( "my-request-id" , payload ) ; console . log ( response . content ) ; Using Different Providers import { callWithRetries , GenericPayload } from "190proof" ; // OpenAI const openaiPayload : GenericPayload = { model : "openai:gpt-5" , messages : \[ { role : "user" , content : "Hello!" } \] , } ; // Anthropic const claudePayload : GenericPayload = { model : "anthropic:claude-sonnet-4-5" , messages : \[ { role : "user" , content : "Hello!" } \] , } ; // Google const geminiPayload : GenericPayload = { model : "google:gemini-2.0-flash" , messages : \[ { role : "user" , content : "Hello!" } \] , } ; // Groq const groqPayload : GenericPayload = { model : "groq:llama-3.3-70b-versatile" , messages : \[ { role : "user" , content : "Hello!" } \] , } ; // OpenRouter const openRouterPayload : GenericPayload = { model : "openrouter:google/gemma-4-31b-it:free" , messages : \[ { role : "user" , content : "Hello!" } \] , } ; const response = await callWithRetries ( "request-id" , claudePayload ) ; With Function Calling const payload : GenericPayload = { model : "openai:gpt-4o" , messages : \[ { role : "user" , content : "What is the capital of France?" , } , \] , functions : \[ { name : "get\_country\_capital" , description : "Get the capital of a given country" , parameters : { type : "object" , properties : { country\_name : { type : "string" , description : "The name of the country" , } , } , required : \[ "country\_name" \] , } , } , \] , } ; const response = await callWithRetries ( "function-call-example" , payload ) ; // response.function\_call contains { name: string, arguments: Record<string, any\> } With Images const payload : GenericPayload = { model : "anthropic:claude-sonnet-4-5" , messages : \[ { role : "user" , content : "What's in this image?" , files : \[ { mimeType : "image/jpeg" , url : "https://example.com/image.jpg" , } , \] , } , \] , } ; const response = await callWithRetries ( "image-example" , payload ) ; How images reach the model depends on the provider. OpenAI, Anthropic, and Google get native image blocks. Groq is text-only: images degrade to an inline Image (url) text reference. OpenRouter sends OpenAI-style image\_url content parts (the remote URL when present, else a data: URI) — but if the model has no vision-capable endpoints, OpenRouter rejects the request with a routing-layer 404, so the retry loop resends the payload with images degraded to the same inline text references Groq gets, and remembers the model (in-process, until restart) so later calls degrade up front. Messages without image attachmen...
**来源信息**
- **来源**:Hacker News:AI 热帖
- **分类**:ai-products
- **发布时间**:2026-09-11 22:01(北京时间)
- **原文**:[打开原文](https://github.com/0xmmo/190proof)