An interactive command-line client for Google's Gemini API, built on .NET 10. It streams responses in real time, keeps multi-turn conversation context, grounds the model in your machine's real environment (time, OS, locale, hardware), and reports honest per-session statistics — including failures and how long they took.
This project contains code generated with the help of large language models and is experimental. It is licensed under AGPL-3.0-or-later.
- Real-time streaming over Server-Sent Events, with live metrics (time-to-first-token, tokens/s).
- Multi-turn conversations with in-session context;
resetstarts fresh. - Environment grounding: the model is told the current local date/time (with a daylight-saving- aware UTC offset), OS and architecture, locale and measurement system, and runtime/hardware facts, gathered with cross-platform managed APIs only — no elevated privileges.
- Robust error handling: Gemini's error envelope is parsed into typed, categorized errors; transient failures (429/503/5xx) are retried automatically, honouring the server's suggested retry delay; free-tier "not available on your plan" responses are recognised and explained.
- Honest telemetry: every attempt is recorded — successes, empties, failures, and cancellations — so the session summary shows success rate, average time-to-failure, average time-to-first-token, and failures grouped by type.
- Sensible model selection: stable, general-purpose text models are preferred; preview and specialised (image/TTS/robotics/…) models are labelled and never chosen as the default.
- Clean console, complete logs: the console is reserved for the UI; all diagnostics go to a per-session log file, so nothing interleaves with your prompts and responses.
- Safe Ctrl+C: cancels the current request without killing the app (press again at the prompt to exit); partial output is kept and a summary is always printed.
Prerequisites: the .NET 10 SDK.
git clone https://github.com/kusl/GeminiClient.git
cd GeminiClient
dotnet build
dotnet run --project GeminiClientConsoleThe solution file is GeminiClient.slnx (the XML solution format). dotnet build, dotnet test
and dotnet restore find it automatically when run from the repository root.
The app needs a Gemini API key in the GeminiSettings section. Configuration is resolved relative
to the executable (so the tool works no matter which directory you launch it from), with the
following precedence (lowest to highest):
appsettings.jsonnext to the executable (ships with a placeholder key).appsettings.{Environment}.jsonnext to the executable.- A per-user config file (recommended home for your real key — survives reinstalls):
- Linux:
$XDG_CONFIG_HOME/gemini-client/appsettings.json(default~/.config/gemini-client/…) - macOS:
~/Library/Application Support/GeminiClient/appsettings.json - Windows:
%APPDATA%\GeminiClient\appsettings.json
- Linux:
- Environment variables, e.g.
GeminiSettings__ApiKey=your-key. dotnet user-secrets(Development only).- Command-line arguments.
Example appsettings.json:
{
"GeminiSettings": {
"ApiKey": "YOUR_GEMINI_API_KEY_HERE",
"BaseUrl": "https://generativelanguage.googleapis.com/",
"DefaultModel": "gemini-2.5-flash",
"TimeoutSeconds": 100,
"MaxRetries": 3,
"RetryBaseDelayMilliseconds": 1000,
"MaxRetryDelayMilliseconds": 60000,
"EnableDetailedLogging": false
}
}| Setting | Default | Meaning |
|---|---|---|
ApiKey |
(none) | Required. Your Gemini API key. |
BaseUrl |
https://generativelanguage.googleapis.com/ |
Absolute http/https endpoint; point it at the mock API for offline work. |
DefaultModel |
(none) | Skips the interactive model picker when set. |
ModelPreference |
Fastest |
Fastest, MostCapable, or Balanced. |
TimeoutSeconds |
100 |
Per-call timeout for non-streaming requests (1–3600). Streaming has no overall timeout so long generations aren't cut off. |
MaxRetries |
3 |
Bound on automatic retries of transient failures (0–10). |
RetryBaseDelayMilliseconds |
1000 |
First backoff step; doubles per attempt with jitter. |
MaxRetryDelayMilliseconds |
60000 |
Ceiling applied to backoff and to server-suggested delays. |
EnableDetailedLogging |
false |
Verbose diagnostics in the session log file. |
Settings are validated on first use; an invalid combination fails fast with every problem listed at once rather than one at a time.
| Command | Description |
|---|---|
exit / quit |
End the session and print a summary |
reset |
Clear the conversation context and start fresh |
model |
Choose a different model |
stats |
Show session statistics so far |
log |
Open the folder containing the conversation and diagnostics logs |
stream |
Toggle streaming vs. standard responses |
help / ? |
Show the command help |
Anything else you type is sent to the model. Press Ctrl+C to cancel a running request; press it again at the prompt to exit.
Each session writes a human-readable conversation log and a separate diagnostics log (framework and library messages, full error detail) to the per-user data directory:
- Linux:
$XDG_DATA_HOME/gemini-client/logs/(default~/.local/share/gemini-client/logs/) - macOS:
~/Library/Application Support/GeminiClient/logs/ - Windows:
%LOCALAPPDATA%\GeminiClient\logs\
If you hit a problem, the diagnostics log is the thing to attach to a bug report.
GeminiClient/— reusable library: HTTP client, streaming, typed error model and parser, retry policy, model ranking, environment-context builder, path resolution, formatting helpers, and session-statistics aggregation.GeminiClientConsole/— the interactive CLI: REPL, rendering, commands, and logging.GeminiMockApi/— a local mock of the Gemini endpoints, including on-demand error scenarios (simulate:429|quota|503|500|400|404|block) for offline testing.GeminiClient.Tests/— xUnit tests covering the library and the console's file-facing pieces.Directory.Build.props/Directory.Packages.props— single source of truth for the version and for dependency versions.global.json— selects the Microsoft Testing Platform runner fordotnet test.docs/adr/— Architecture Decision Records explaining the design.
dotnet testThe test project uses xunit.v3 4.x on the Microsoft Testing Platform. The test assembly is itself an executable, so you can also run it directly for faster, filtered runs:
dotnet run --project GeminiClient.Tests -- --filter-method "*Retry*"dotnet run --project GeminiMockApiThen set GeminiSettings:BaseUrl to the address the mock prints (for example
http://localhost:5057/) and use any non-empty API key. Prompt the model with
simulate:429, simulate:quota, simulate:503, simulate:500, simulate:400, simulate:404, or
simulate:block to exercise each error path. The mock also serves its OpenAPI document at
/openapi/v1.json.
dotnet publish GeminiClientConsole/GeminiClientConsole.csproj \
--configuration Release \
--runtime linux-x64 \
--self-contained true \
--output ./publish/linux-x64 \
-p:PublishSingleFile=true \
-p:PublishTrimmed=trueSwap linux-x64 for win-x64, osx-arm64, or any other supported runtime identifier.
The version is defined once, in Directory.Build.props. Continuous integration appends the build
run number to produce the published version (e.g. 0.1.0 → 0.1.0.<run>).
Licensed under the GNU Affero General Public License v3.0 or later (AGPL-3.0-or-later).