{"id":51487454,"url":"https://github.com/dev-tayyab/plume","last_synced_at":"2026-07-07T08:00:44.535Z","repository":{"id":355068917,"uuid":"1226291480","full_name":"dev-tayyab/Plume","owner":"dev-tayyab","description":"The minimalist, resilient AI client for .NET — one unified API across OpenAI, Anthropic Claude, Google Gemini, and Ollama with built-in failover. Tiny, AOT-friendly, .NET 8/9/10. ","archived":false,"fork":false,"pushed_at":"2026-05-02T11:47:41.000Z","size":125,"stargazers_count":2,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2026-06-03T22:04:38.739Z","etag":null,"topics":["ai","anthropic","aot","chatgpt","claude","csharp","dotnet","dotnet-library","failover","gemini","llm","nuget","ollama","openai","resilience","streaming"],"latest_commit_sha":null,"homepage":"https://www.nuget.org/packages/Plume","language":"C#","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/dev-tayyab.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","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,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2026-05-01T07:53:29.000Z","updated_at":"2026-05-15T13:14:00.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/dev-tayyab/Plume","commit_stats":null,"previous_names":["dev-tayyab/plume"],"tags_count":1,"template":false,"template_full_name":null,"purl":"pkg:github/dev-tayyab/Plume","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/dev-tayyab%2FPlume","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/dev-tayyab%2FPlume/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/dev-tayyab%2FPlume/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/dev-tayyab%2FPlume/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/dev-tayyab","download_url":"https://codeload.github.com/dev-tayyab/Plume/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/dev-tayyab%2FPlume/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":35219594,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-26T15:22:16.424Z","status":"online","status_checked_at":"2026-07-07T02:00:07.222Z","response_time":90,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"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":["ai","anthropic","aot","chatgpt","claude","csharp","dotnet","dotnet-library","failover","gemini","llm","nuget","ollama","openai","resilience","streaming"],"created_at":"2026-07-07T08:00:43.374Z","updated_at":"2026-07-07T08:00:44.470Z","avatar_url":"https://github.com/dev-tayyab.png","language":"C#","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Plume 🪶\n\n**The minimalist, resilient AI client for .NET.**\nOne unified API. Multiple providers. Built-in failover.\n\n[![NuGet](https://img.shields.io/nuget/v/Plume.svg?label=Plume)](https://www.nuget.org/packages/Plume)\n[![NuGet Downloads](https://img.shields.io/nuget/dt/Plume.svg)](https://www.nuget.org/packages/Plume)\n[![CI](https://github.com/dev-tayyab/Plume/actions/workflows/ci.yml/badge.svg)](https://github.com/dev-tayyab/Plume/actions/workflows/ci.yml)\n[![License: MIT](https://img.shields.io/badge/license-MIT-blue.svg)](https://github.com/dev-tayyab/Plume/blob/main/LICENSE)\n[![.NET](https://img.shields.io/badge/.NET-8.0%20%7C%209.0%20%7C%2010.0-512BD4)](https://dotnet.microsoft.com/)\n\n| Package | Version |\n| --- | --- |\n| [Plume](https://www.nuget.org/packages/Plume) | [![NuGet](https://img.shields.io/nuget/v/Plume.svg)](https://www.nuget.org/packages/Plume) |\n| [Plume.OpenAI](https://www.nuget.org/packages/Plume.OpenAI) | [![NuGet](https://img.shields.io/nuget/v/Plume.OpenAI.svg)](https://www.nuget.org/packages/Plume.OpenAI) |\n| [Plume.Anthropic](https://www.nuget.org/packages/Plume.Anthropic) | [![NuGet](https://img.shields.io/nuget/v/Plume.Anthropic.svg)](https://www.nuget.org/packages/Plume.Anthropic) |\n| [Plume.Google](https://www.nuget.org/packages/Plume.Google) | [![NuGet](https://img.shields.io/nuget/v/Plume.Google.svg)](https://www.nuget.org/packages/Plume.Google) |\n| [Plume.Ollama](https://www.nuget.org/packages/Plume.Ollama) | [![NuGet](https://img.shields.io/nuget/v/Plume.Ollama.svg)](https://www.nuget.org/packages/Plume.Ollama) |\n\n```csharp\nusing var http = new HttpClient();\nvar ai = PlumeClient.CreateBuilder()\n    .Use(new OpenAIProvider(http, new() { ApiKey = \"sk-...\" }))\n    .WithDefaultModel(\"gpt-4o-mini\")\n    .Build();\n\nstring answer = await ai.AskAsync(\"What is the capital of France?\");\n```\n\nThat's it. From zero to a working LLM call in two lines. No `KernelBuilder`, no plugins, no ceremony.\n\n---\n\n## Why Plume?\n\nThe .NET AI ecosystem is dominated by heavyweight frameworks designed for enterprise. Plume is for the rest of us:\n\n- **Tiny.** Core package has minimal dependencies. No Azure SDK pulled in transitively.\n- **Multi-provider.** OpenAI, Anthropic Claude, Google Gemini, Ollama — same API, swap at will.\n- **Resilient by default.** Provider down? Plume fails over silently to the next one. Your app keeps responding.\n- **Async-first.** `IAsyncEnumerable` streaming. `CancellationToken` everywhere. Modern .NET 8+.\n- **AOT-ready.** Source-generated JSON, trim-safe, Native AOT compatible.\n\n## Install\n\n```bash\ndotnet add package Plume\ndotnet add package Plume.OpenAI       # optional\ndotnet add package Plume.Anthropic    # optional\ndotnet add package Plume.Google       # optional\ndotnet add package Plume.Ollama       # optional\n```\n\nInstall only the providers you use. Plume itself does not depend on any specific provider.\n\n## Quick start (DI / ASP.NET)\n\n```csharp\nusing Plume.DependencyInjection;\nusing Plume.OpenAI;\n\nbuilder.Services.AddPlume(options =\u003e\n{\n    options.UseOpenAI(builder.Configuration[\"OpenAI:ApiKey\"]!);\n    options.DefaultModel = \"gpt-4o-mini\";\n});\n\n// Anywhere in your app:\npublic class SummaryService(IPlumeClient ai)\n{\n    public Task\u003cstring\u003e Summarize(string text) =\u003e\n        ai.AskAsync($\"Summarize in 2 lines: {text}\");\n}\n```\n\n## Streaming\n\n```csharp\nawait foreach (var chunk in ai.StreamAsync(\"Write a haiku about rain\"))\n    Console.Write(chunk);\n```\n\n## Multi-turn conversations\n\n```csharp\nvar chat = ai.NewChat(system: \"You are a senior C# code reviewer.\");\n\nawait chat.AskAsync(\"Review: public string Hi() =\u003e \\\"hi\\\";\");\nawait chat.AskAsync(\"How can I make it more idiomatic?\");\n\n// Persist or restore\nforeach (var message in chat.History)\n    SaveToDatabase(message);\n```\n\n## Failover — the killer feature\n\n```csharp\nusing Plume.Anthropic;\nusing Plume.OpenAI;\nusing Plume.Google;\nusing Plume.Ollama;\n\nbuilder.Services.AddPlume(options =\u003e\n{\n    options.UseAnthropic(anthropicKey);          // primary\n    options.FallbackToOpenAI(openAiKey);         // if Anthropic fails transiently\n    options.FallbackToGoogle(googleKey);         // then Google\n    options.FallbackToOllama();                  // local last resort\n\n    options.DefaultModel = \"claude-sonnet-4\";\n});\n```\n\nIf Anthropic returns 429 or 503, Plume silently fails over to OpenAI, then Google, then your local Ollama. Your application keeps responding. No code changes, no try/catch. **This is the gap nothing else in the .NET ecosystem fills.**\n\nWhat counts as transient (failover happens):\n- HTTP 429 (rate limit) — uses `Retry-After` if provided\n- HTTP 503/504/502/408 (server unavailable, gateway timeout)\n- `HttpRequestException` (network failure)\n- Timeouts\n\nWhat does NOT count as transient (failover does NOT happen):\n- HTTP 4xx other than 429 (bad request, auth failure, not found)\n- Cancellation requested by your `CancellationToken`\n\n## Provider-specific options (strongly typed)\n\n```csharp\nvar answer = await ai.AskAsync(\"Generate a story\",\n    new AskOptions\n    {\n        Temperature = 0.9,\n        Extensions = new OpenAIExtensions\n        {\n            FrequencyPenalty = 0.5,\n            Seed = 42\n        }\n    });\n```\n\nIntelliSense works. Compile-time safety. No stringly-typed dictionaries.\nProvider-specific extensions are silently ignored if the request fails over to a different provider — so you can use them safely with failover enabled.\n\n## Supported providers\n\n| Provider | Package | Model prefix | Streaming |\n| -------- | ------- | ------------ | :-------: |\n| OpenAI (and Azure / OpenRouter / compatible) | `Plume.OpenAI` | `gpt-*`, `o1*`, `o3*`, `o4*` | ✅ |\n| Anthropic Claude | `Plume.Anthropic` | `claude-*` | ✅ |\n| Google Gemini | `Plume.Google` | `gemini*` | ✅ |\n| Ollama (local models) | `Plume.Ollama` | any | ✅ |\n\n## Samples\n\nRunnable end-to-end samples live in [`samples/`](https://github.com/dev-tayyab/Plume/tree/main/samples). Each is a single-file program — clone the repo, set the relevant `*_API_KEY` env var, and `dotnet run`.\n\n| Sample | What it shows |\n| --- | --- |\n| [Console](https://github.com/dev-tayyab/Plume/tree/main/samples/Plume.Sample.Console) | Streaming + multi-turn chat against OpenAI in ~20 lines |\n| [Console.Google](https://github.com/dev-tayyab/Plume/tree/main/samples/Plume.Sample.Console.Google) | Same flow against Google Gemini — drop-in provider swap |\n| [Failover](https://github.com/dev-tayyab/Plume/tree/main/samples/Plume.Sample.Failover) | Anthropic → OpenAI → Google → Ollama failover chain |\n| [ToolCalling](https://github.com/dev-tayyab/Plume/tree/main/samples/Plume.Sample.ToolCalling) | Provider-agnostic function/tool calling |\n| [StructuredOutput](https://github.com/dev-tayyab/Plume/tree/main/samples/Plume.Sample.StructuredOutput) | Source-generated typed responses (JSON schema, no reflection) |\n| [Embeddings](https://github.com/dev-tayyab/Plume/tree/main/samples/Plume.Sample.Embeddings) | Embedding vectors with the same provider abstraction |\n\n## Status\n\n🚧 **v0.1.0-alpha** — under active development, API may evolve before 1.0.\n\nThe roadmap:\n\n| Version | Focus |\n| :-----: | --- |\n| 0.1 | Core API: ask, stream, chat, failover. 4 providers shipping. |\n| 0.2 | Source-generated typed responses. Tool calling. |\n| 0.3 | MCP client support. Embeddings. |\n| 0.4 | Vision/multimodal input. |\n| 0.5 | Retrieval helpers (RAG). |\n| 1.0 | API freeze. Production-ready. |\n\n## Contributing\n\nIssues and PRs welcome.\n\n## License\n\nMIT. Use it freely in commercial projects.\n\n---\n\nBuilt with ❤️ for .NET developers who want their AI code to read like it was written, not generated.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdev-tayyab%2Fplume","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fdev-tayyab%2Fplume","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdev-tayyab%2Fplume/lists"}