RuleStack

Configs

Stacks

Compare

Diff

RuleStack

Configs

Stacks

Compare

Diff

Read API

RuleStack

Configs

Stacks

Compare

Diff

Read API

Diff/sethrobinson-ugtlive-cursor-rules-service-integration-patterns ↔ sethrobinson-ugtlive-agents

Comparison

A · Cursor rules · SethRobinson/UGTLiveB · AGENTS.md · SethRobinson/UGTLive
What each file covers, counted
DimensionSharedOnly in AOnly in BOverlap
Sections04170%
Commands0030%
Section tags13413%

What each file covers

Sections

0 shared · 41 only in A · 7 only in B
  • − Service Integration Patterns
  • − Translation Service Interface
  • − ITranslationService Pattern
  • − TranslationResult Structure
  • − HTTP Client Usage
  • − Service HTTP Client Pattern
  • − Request Building
  • − Error Handling in Services
  • − HTTP Error Handling Pattern
  • − Exception Handling
  • − Service Factory Pattern
  • − TranslationServiceFactory
  • − OCR Service Integration
  • − Windows OCR Pattern
  • − Google Vision OCR Pattern
  • − Python OCR Service Pattern (HTTP-based)
  • − Text-to-Speech Integration
  • − TTS Service Pattern
  • − Audio Processing
  • − NAudio Integration
  • − Audio Preloading
  • − Real-time Audio Streaming
  • − WebSocket Pattern
  • − Service Configuration
  • − Python OCR Service Configuration
  • − Translation Service Configuration
  • − Retry Logic
  • − Exponential Backoff Pattern
  • − Timeout Handling
  • − Request Timeout
  • − Cancellation Token
  • − JSON Serialization
  • − System.Text.Json Pattern
  • − Service Health Checks
  • − Connection Testing
  • − Python Service Health Check
  • − Service Discovery
  • − Python Service Discovery
  • − Service Lifecycle Management
  • − Starting Services
  • − Stopping Services
  • + AGENTS.md
  • + Shared Project Memory
  • + Testing
  • + Cloud LLM Model Maintenance
  • + Feature Index
  • + Security
  • + Git

Commands

0 shared · 0 only in A · 3 only in B
  • + dotnet build .\UGTLive.sln --configuration Release
  • + git commit
  • + git push

Section tags

1 shared · 3 only in A · 4 only in B
  • − build
  • − code-style
  • − architecture
  • + git-pr
  • + security
  • + performance
  • + do-not
  •   test

Line diff

+38 added−496 removed17 unchanged3.3% identical
SethRobinson/UGTLive · .cursor/rules/service-integration-patterns.mdc
@@ −1 @@
1---
2description: Patterns for integrating external services (APIs, OCR, TTS)
3globs: ["**/*Service.cs", "**/*Manager.cs"]
4alwaysApply: false
5---
6 
7# Service Integration Patterns
8 
9## Translation Service Interface
10 
11### ITranslationService Pattern
12All translation services implement a common interface:
 
 
 
 
 
13 
14```csharp
15public interface ITranslationService
16{
17 Task<TranslationResult?> TranslateAsync(
18 string sourceText,
19 string targetLanguage,
20 string context);
21}
22```
23 
24### TranslationResult Structure
25```csharp
26public class TranslationResult
27{
28 public string TranslatedText { get; set; } = "";
29 public string SourceLanguage { get; set; } = "";
30 public double Confidence { get; set; } = 1.0;
31}
32```
33 
34## HTTP Client Usage
 
 
 
 
35 
36### Service HTTP Client Pattern
37Each service creates its own HttpClient:
38 
39```csharp
40public class GeminiTranslationService : ITranslationService
41{
42 private static readonly HttpClient _httpClient = new HttpClient();
43 private readonly string _apiKey;
44
45 public GeminiTranslationService()
46 {
47 _apiKey = ConfigManager.Instance.GetGeminiApiKey();
48 }
49
50 public async Task<TranslationResult?> TranslateAsync(string text, string lang, string context)
51 {
52 // Implementation
53 }
54}
55```
56 
57### Request Building
58```csharp
59var requestBody = new
60{
61 contents = new[]
62 {
63 new
64 {
65 parts = new[]
66 {
67 new { text = prompt }
68 }
69 }
70 }
71};
72 
73var json = JsonSerializer.Serialize(requestBody);
74var content = new StringContent(json, Encoding.UTF8, "application/json");
75var response = await _httpClient.PostAsync(url, content);
76```
77 
78## Error Handling in Services
 
79 
80### HTTP Error Handling Pattern
81```csharp
82if (response.IsSuccessStatusCode)
83{
84 var result = await response.Content.ReadAsStringAsync();
85 return ParseResult(result);
86}
87else
88{
89 string errorMessage = await response.Content.ReadAsStringAsync();
90 Console.WriteLine($"API error: {response.StatusCode}, {errorMessage}");
91
92 // Try to parse JSON error
93 try
94 {
95 using JsonDocument errorDoc = JsonDocument.Parse(errorMessage);
96 if (errorDoc.RootElement.TryGetProperty("error", out JsonElement errorElement))
97 {
98 // Extract detailed error
99 return null;
100 }
101 }
102 catch (JsonException)
103 {
104 // Fallback to raw message
105 }
106
107 // Show user-friendly error
108 ErrorPopupManager.ShowError("Translation Error", errorMessage);
109
110 return null;
111}
112```
113 
114### Exception Handling
115```csharp
116try
117{
118 var response = await _httpClient.PostAsync(url, content);
119 // Process response
120}
121catch (Exception ex)
122{
123 Console.WriteLine($"Service error: {ex.Message}");
124 LogManager.Instance.LogError("Translation failed", ex);
125
126 ErrorPopupManager.ShowError("Translation Error", ex.Message);
127
128 return null;
129}
130```
131 
132## Service Factory Pattern
 
 
 
 
 
133 
134### TranslationServiceFactory
135```csharp
136public static class TranslationServiceFactory
137{
138 public static ITranslationService CreateService(string serviceName)
139 {
140 return serviceName switch
141 {
142 "Gemini" => new GeminiTranslationService(),
143 "ChatGPT" => new ChatGptTranslationService(),
144 "Ollama" => new OllamaTranslationService(),
145 "Google Translate" => new GoogleTranslateService(),
146 "llama.cpp" => new LlamaCppTranslationService(),
147 _ => throw new ArgumentException($"Unknown service: {serviceName}")
148 };
149 }
150}
151```
152 
153## OCR Service Integration
154 
155### Windows OCR Pattern
156```csharp
157public class WindowsOCRManager
158{
159 public async Task<List<TextObject>> ProcessImageAsync(Bitmap bitmap)
160 {
161 return await Task.Run(() =>
162 {
163 // Convert bitmap to Windows bitmap
164 // Process with Windows OCR API
165 // Return TextObject list
166 });
167 }
168}
169```
170 
171### Google Vision OCR Pattern
172```csharp
173public class GoogleVisionOCRService
174{
175 public async Task<List<TextObject>> ProcessImageAsync(Bitmap bitmap)
176 {
177 // Convert bitmap to base64
178 // Send to Google Vision API
179 // Parse response and return TextObject list
180 }
181}
182```
183 
184### Python OCR Service Pattern (HTTP-based)
185```csharp
186// Services are managed via PythonServicesManager
187// Discover services on startup
188PythonServicesManager.Instance.DiscoverServices();
189 
190// Get service by name
191var service = PythonServicesManager.Instance.GetServiceByName("EasyOCR");
192 
193// Check if service is running
194if (!service.IsRunning)
195{
196 bool isRunning = await service.CheckIsRunningAsync();
197 if (!isRunning)
198 {
199 // Show error dialog or start service
200 await service.StartAsync(showWindow: false);
201 }
202}
203 
204// Process image with HTTP service
205private async Task<string?> ProcessImageWithHttpServiceAsync(byte[] imageBytes, string serviceName, string language)
206{
207 var service = PythonServicesManager.Instance.GetServiceByName(serviceName);
208 if (service == null) return null;
209
210 // Build URL with query parameters
211 string langParam = MapLanguageForService(language);
212 string url = $"{service.ServerUrl}:{service.Port}/process?lang={langParam}&char_level=true";
213
214 // Add service-specific parameters (e.g., MangaOCR)
215 if (serviceName == "MangaOCR")
216 {
217 url += $"&min_region_width={minWidth}&min_region_height={minHeight}&overlap_allowed_percent={overlapPercent}";
218 }
219
220 // Add PaddleOCR-specific parameters
221 if (serviceName == "PaddleOCR")
222 {
223 bool useAngleCls = ConfigManager.Instance.GetBoolValue(ConfigManager.PADDLE_OCR_USE_ANGLE_CLS, false);
224 url += $"&use_angle_cls={useAngleCls.ToString().ToLower()}";
225 }
226
227 // Send binary image data
228 var content = new ByteArrayContent(imageBytes);
229 content.Headers.ContentType = new MediaTypeHeaderValue("application/octet-stream");
230
231 using var request = new HttpRequestMessage(HttpMethod.Post, url);
232 request.Content = content;
233 request.Headers.ConnectionClose = false; // Enable HTTP keep-alive
234 var response = await _httpClient.SendAsync(request);
235
236 if (!response.IsSuccessStatusCode)
237 {
238 service.MarkAsNotRunning();
239 return null;
240 }
241
242 return await response.Content.ReadAsStringAsync();
243}
244```
245 
246## Text-to-Speech Integration
247 
248### TTS Service Pattern
249```csharp
250public interface ITTSService
251{
252 Task<byte[]?> SynthesizeAsync(string text, string language);
253}
254 
255public class GoogleTTSService : ITTSService
256{
257 public async Task<byte[]?> SynthesizeAsync(string text, string language)
258 {
259 var url = $"https://texttospeech.googleapis.com/v1/text:synthesize";
260 // Build request and get audio bytes
261 return audioBytes;
262 }
263}
264```
265 
266## Audio Processing
267 
268### NAudio Integration
269```csharp
270using NAudio.Wave;
271 
272public class AudioPlaybackManager
273{
274 private WaveOutEvent? _waveOut;
275
276 public void PlayAudio(byte[] audioData)
277 {
278 _waveOut?.Stop();
279 _waveOut?.Dispose();
280
281 using var ms = new MemoryStream(audioData);
282 using var reader = new Mp3FileReader(ms);
283
284 _waveOut = new WaveOutEvent();
285 _waveOut.Init(reader);
286 _waveOut.Play();
287 }
288}
289```
290 
291### Audio Preloading
292```csharp
293public class AudioPreloadService
294{
295 // Preload audio for source and target languages
296 public async Task PreloadAudioAsync(string text, string language, string service)
297 {
298 // Generate audio and cache it
299 // Used for faster playback later
300 }
301}
302```
303 
304## Real-time Audio Streaming
305 
306### WebSocket Pattern
307```csharp
308public class OpenAIRealtimeAudioService
309{
310 private ClientWebSocket? _webSocket;
311
312 public async Task ConnectAsync()
313 {
314 _webSocket = new ClientWebSocket();
315 await _webSocket.ConnectAsync(new Uri("wss://api.openai.com/v1/realtime"), CancellationToken.None);
316
317 // Start receiving loop
318 _ = Task.Run(ReceiveLoop);
319 }
320
321 private async Task ReceiveLoop()
322 {
323 var buffer = new byte[4096];
324 while (_webSocket?.State == WebSocketState.Open)
325 {
326 var result = await _webSocket.ReceiveAsync(new ArraySegment<byte>(buffer), CancellationToken.None);
327 ProcessMessage(buffer, result.Count);
328 }
329 }
330}
331```
332 
333## Service Configuration
334 
335### Python OCR Service Configuration
336Python OCR services are configured via `service_config.txt` files in each service directory:
337 
338```csharp
339// Service discovery reads service_config.txt
340// Format: key|value|
341// Fields: service_name, venv_name, port, description, version, author, github_url, local_only, server_url
342 
343// C# PythonService.ParseFromConfig() reads the config file
344var service = PythonService.ParseFromConfig(serviceDirectory);
345 
346// Service properties available:
347// - service.ServiceName (from service_name)
348// - service.VenvName (from venv_name)
349// - service.Port (from port)
350// - service.Description, Version, Author, GithubUrl, etc.
351```
352 
353### Translation Service Configuration
354Translation services read their configuration from ConfigManager:
355 
356```csharp
357public class GeminiTranslationService
358{
359 private readonly string _apiKey;
360 private readonly string _model;
361 private readonly string _prompt;
362
363 public GeminiTranslationService()
364 {
365 _apiKey = ConfigManager.Instance.GetGeminiApiKey();
366 _model = ConfigManager.Instance.GetGeminiModel();
367 _prompt = ConfigManager.Instance.GetGeminiPrompt();
368 }
369}
370```
371 
372## Retry Logic
373 
374### Exponential Backoff Pattern
375```csharp
376public async Task<TranslationResult?> TranslateWithRetryAsync(string text, int maxRetries = 3)
377{
378 for (int attempt = 0; attempt < maxRetries; attempt++)
379 {
380 try
381 {
382 return await TranslateAsync(text);
383 }
384 catch (Exception ex)
385 {
386 if (attempt == maxRetries - 1)
387 {
388 throw;
389 }
390
391 int delay = (int)Math.Pow(2, attempt) * 1000; // Exponential backoff
392 await Task.Delay(delay);
393 }
394 }
395
396 return null;
397}
398```
399 
400## Timeout Handling
401 
402### Request Timeout
403```csharp
404private static readonly HttpClient _httpClient = new HttpClient
405{
406 Timeout = TimeSpan.FromSeconds(30)
407};
408```
409 
410### Cancellation Token
411```csharp
412using var cts = new CancellationTokenSource(TimeSpan.FromSeconds(30));
413var response = await _httpClient.PostAsync(url, content, cts.Token);
414```
415 
416## JSON Serialization
417 
418### System.Text.Json Pattern
419```csharp
420using System.Text.Json;
421 
422var options = new JsonSerializerOptions
423{
424 PropertyNamingPolicy = JsonNamingPolicy.CamelCase,
425 WriteIndented = false
426};
427 
428var json = JsonSerializer.Serialize(requestObject, options);
429var responseObject = JsonSerializer.Deserialize<ResponseType>(jsonString, options);
430```
431 
432## Service Health Checks
433 
434### Connection Testing
435```csharp
436public async Task<bool> TestConnectionAsync()
437{
438 try
439 {
440 var testRequest = new { text = "test" };
441 var result = await TranslateAsync("test", "en", "");
442 return result != null;
443 }
444 catch
445 {
446 return false;
447 }
448}
449```
450 
451### Python Service Health Check
452```csharp
453public async Task<bool> CheckServiceHealthAsync(PythonService service)
454{
455 try
456 {
457 var url = $"{service.ServerUrl}:{service.Port}/health";
458 var response = await _httpClient.GetAsync(url);
459 return response.IsSuccessStatusCode;
460 }
461 catch
462 {
463 return false;
464 }
465}
466```
467 
468## Service Discovery
469 
470### Python Service Discovery
471```csharp
472// Automatically discovers services in app/services/
473PythonServicesManager.Instance.DiscoverServices();
474 
475// Get all discovered services
476var services = PythonServicesManager.Instance.GetAllServices();
477 
478// Get service by name
479var easyOCR = PythonServicesManager.Instance.GetServiceByName("EasyOCR");
480```
481 
482## Service Lifecycle Management
483 
484### Starting Services
485```csharp
486public async Task<bool> StartServiceAsync(PythonService service, bool showWindow = false)
487{
488 if (service.IsRunning)
489 {
490 return true;
491 }
492
493 // Start Python server process
494 // Wait for health check
495 // Mark as running
496}
497```
498 
499### Stopping Services
500```csharp
501public async Task StopServiceAsync(PythonService service)
502{
503 if (!service.IsRunning)
504 {
505 return;
506 }
507
508 // Send shutdown request
509 // Wait for process to exit
510 // Mark as not running
511}
512```
513 
SethRobinson/UGTLive · AGENTS.md
@@ +1 @@
1# AGENTS.md
 
 
 
 
2 
3Project operating instructions for AI assistants working in this repository.
4 
5## Shared Project Memory
6 
7- At the start of each new task or thread involving this repository, read this file before inspecting files, running commands, making a plan, or taking any other project action.
8- Treat follow-up replies in the same continuous task as part of that task. Do not reread this file unless the repository or working directory changes, this file is modified, or its instructions are no longer available in context.
9- Treat this file as the shared project memory for AI assistants.
10- Do not rely on vendor-specific, proprietary, or hidden memory systems for project facts, preferences, or operating instructions. (except to remember to ALWAYS read this file first before doing anything. Remember that.)
11- Update this file with important repo-specific information learned during work, including build commands, test commands, conventions, decisions, pitfalls, and current project preferences.
12- Keep this file accurate and current. Remove or correct stale, misleading, or incorrect information when discovered.
13- If information is temporary or uncertain, label it clearly rather than presenting it as permanent fact.
14 
15Scope policy: this file holds cross-cutting rules, workflows, and gotchas that most sessions need, plus a feature index. Keep it around 30 KB. Feature deep-dives live in `docs/<topic>.md`: before working on a feature listed in the index, read its doc; when finishing feature work, update that doc and keep the index entry here to one or two lines (where it lives + the non-obvious constraint). Cross-cutting rules and new gotchas still land here directly. When a change makes anything stale, here or in a linked doc, update it in the same change.
 
 
 
 
 
 
 
 
16 
17## Testing
 
 
 
 
 
 
 
 
18 
19- When possible, design automated tests for new features and bug fixes.
20- Run relevant automated tests after finishing changes to guard against regressions.
21- If tests cannot be run or do not exist, state that clearly in the handoff and describe any manual verification performed.
22- Always finish project changes by building the Release configuration: `dotnet build .\UGTLive.sln --configuration Release`.
23- If a running UGTLive process prevents the Release build, capture its executable/command line, stop it, complete the build, and restart it afterward. Do not start UGTLive if it was not running before the build.
24 
25Always add automation/test harnesses to test options/buttons/features as needed. Document them.
 
26 
27## Cloud LLM Model Maintenance
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
28 
29- Cloud and CLI model picker presets live in `src/SettingsWindow.xaml`; their fallback/default values live in `src/ConfigManager.cs`, `src/ConfigManager.Translation.cs`, and `src/SettingsWindow.TranslationSettings.cs`.
30- Subscription-backed CLI providers display as `Anthropic Sub`, `OpenAI Sub`, and `Gemini CLI (Enterprise)`, but their stable internal IDs remain `ClaudeCli`, `CodexCli`, and `GeminiCli`; use `ComboBoxItem.Tag` for the internal ID. Google ended personal/free/AI Pro/AI Ultra access through Gemini CLI on June 18, 2026; do not replace it with Antigravity CLI until `agy -p` reliably exposes captured stdout to Windows parent processes (see `docs/settings-connection-tests.md`).
31- Keep provider-specific capability handling in the matching translation service. In particular, Anthropic model generations use different manual/adaptive thinking request shapes.
32- Verify model IDs and request compatibility against current official provider documentation. Verify OpenRouter-prefixed slugs against its `/api/v1/models` catalog before adding presets.
 
 
 
 
 
 
 
 
 
 
 
33 
34## Feature Index
 
 
 
35 
36- Settings API/model/voice tests: see `docs/settings-connection-tests.md`. UI buttons and `--test-settings-connection` must continue to call the shared `SettingsConnectionTester` implementation.
37- OpenAI All In One Snap translation: see `docs/openai-all-in-one.md`. It is a Snap-only, visual-only `gpt-image-2` Image Edits path; Auto and realtime processing must remain on the standard OCR pipeline.
38 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
39 
40## Security
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
41 
42- Never commit sensitive data, including credentials, tokens, passwords, private keys, cookies, customer data, personal data, or machine-specific authentication material.
43- If an AI assistant needs authentication data or other secrets for local work, use `agents_secret.md` for those notes.
44- `agents_secret.md` must stay ignored by git and must not be committed.
45- Do not put secrets in commit messages, logs, issue text, pull request descriptions, generated docs, or other tracked files.
46- Configuration logging must pass key names through `ConfigManager.IsSensitiveConfigKey`; never print raw secret values in startup or harness output.
47- Before committing, review staged changes for accidental secrets.
48 
49## Git
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
50 
51- Never add OpenAI/Codex/Claude etc as a co-author on git commits.
52- NEVER `git commit` unless explicitly told to commit.
53- NEVER `git push` unless explicitly told to push. "Commit" means commit
54 locally only; committing is not permission to push.
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
55 
@@ −1 +1 @@
1−---
2−description: Patterns for integrating external services (APIs, OCR, TTS)
3−globs: ["**/*Service.cs", "**/*Manager.cs"]
4−alwaysApply: false
5−---
1+# AGENTS.md
62  
7−# Service Integration Patterns
3+Project operating instructions for AI assistants working in this repository.
84  
9−## Translation Service Interface
5+## Shared Project Memory
106  
11−### ITranslationService Pattern
12−All translation services implement a common interface:
7+- At the start of each new task or thread involving this repository, read this file before inspecting files, running commands, making a plan, or taking any other project action.
8+- Treat follow-up replies in the same continuous task as part of that task. Do not reread this file unless the repository or working directory changes, this file is modified, or its instructions are no longer available in context.
9+- Treat this file as the shared project memory for AI assistants.
10+- Do not rely on vendor-specific, proprietary, or hidden memory systems for project facts, preferences, or operating instructions. (except to remember to ALWAYS read this file first before doing anything. Remember that.)
11+- Update this file with important repo-specific information learned during work, including build commands, test commands, conventions, decisions, pitfalls, and current project preferences.
12+- Keep this file accurate and current. Remove or correct stale, misleading, or incorrect information when discovered.
13+- If information is temporary or uncertain, label it clearly rather than presenting it as permanent fact.
1314  
14−```csharp
15−public interface ITranslationService
16−{
17− Task<TranslationResult?> TranslateAsync(
18− string sourceText,
19− string targetLanguage,
20− string context);
21−}
22−```
15+Scope policy: this file holds cross-cutting rules, workflows, and gotchas that most sessions need, plus a feature index. Keep it around 30 KB. Feature deep-dives live in `docs/<topic>.md`: before working on a feature listed in the index, read its doc; when finishing feature work, update that doc and keep the index entry here to one or two lines (where it lives + the non-obvious constraint). Cross-cutting rules and new gotchas still land here directly. When a change makes anything stale, here or in a linked doc, update it in the same change.
2316  
24−### TranslationResult Structure
25−```csharp
26−public class TranslationResult
27−{
28− public string TranslatedText { get; set; } = "";
29− public string SourceLanguage { get; set; } = "";
30− public double Confidence { get; set; } = 1.0;
31−}
32−```
17+## Testing
3318  
34−## HTTP Client Usage
19+- When possible, design automated tests for new features and bug fixes.
20+- Run relevant automated tests after finishing changes to guard against regressions.
21+- If tests cannot be run or do not exist, state that clearly in the handoff and describe any manual verification performed.
22+- Always finish project changes by building the Release configuration: `dotnet build .\UGTLive.sln --configuration Release`.
23+- If a running UGTLive process prevents the Release build, capture its executable/command line, stop it, complete the build, and restart it afterward. Do not start UGTLive if it was not running before the build.
3524  
36−### Service HTTP Client Pattern
37−Each service creates its own HttpClient:
25+Always add automation/test harnesses to test options/buttons/features as needed. Document them.
3826  
39−```csharp
40−public class GeminiTranslationService : ITranslationService
41−{
42− private static readonly HttpClient _httpClient = new HttpClient();
43− private readonly string _apiKey;
44−
45− public GeminiTranslationService()
46− {
47− _apiKey = ConfigManager.Instance.GetGeminiApiKey();
48− }
49−
50− public async Task<TranslationResult?> TranslateAsync(string text, string lang, string context)
51− {
52− // Implementation
53− }
54−}
55−```
27+## Cloud LLM Model Maintenance
5628  
57−### Request Building
58−```csharp
59−var requestBody = new
60−{
61− contents = new[]
62− {
63− new
64− {
65− parts = new[]
66− {
67− new { text = prompt }
68− }
69− }
70− }
71−};
29+- Cloud and CLI model picker presets live in `src/SettingsWindow.xaml`; their fallback/default values live in `src/ConfigManager.cs`, `src/ConfigManager.Translation.cs`, and `src/SettingsWindow.TranslationSettings.cs`.
30+- Subscription-backed CLI providers display as `Anthropic Sub`, `OpenAI Sub`, and `Gemini CLI (Enterprise)`, but their stable internal IDs remain `ClaudeCli`, `CodexCli`, and `GeminiCli`; use `ComboBoxItem.Tag` for the internal ID. Google ended personal/free/AI Pro/AI Ultra access through Gemini CLI on June 18, 2026; do not replace it with Antigravity CLI until `agy -p` reliably exposes captured stdout to Windows parent processes (see `docs/settings-connection-tests.md`).
31+- Keep provider-specific capability handling in the matching translation service. In particular, Anthropic model generations use different manual/adaptive thinking request shapes.
32+- Verify model IDs and request compatibility against current official provider documentation. Verify OpenRouter-prefixed slugs against its `/api/v1/models` catalog before adding presets.
7233  
73−var json = JsonSerializer.Serialize(requestBody);
74−var content = new StringContent(json, Encoding.UTF8, "application/json");
75−var response = await _httpClient.PostAsync(url, content);
76−```
34+## Feature Index
7735  
78−## Error Handling in Services
36+- Settings API/model/voice tests: see `docs/settings-connection-tests.md`. UI buttons and `--test-settings-connection` must continue to call the shared `SettingsConnectionTester` implementation.
37+- OpenAI All In One Snap translation: see `docs/openai-all-in-one.md`. It is a Snap-only, visual-only `gpt-image-2` Image Edits path; Auto and realtime processing must remain on the standard OCR pipeline.
7938  
80−### HTTP Error Handling Pattern
81−```csharp
82−if (response.IsSuccessStatusCode)
83−{
84− var result = await response.Content.ReadAsStringAsync();
85− return ParseResult(result);
86−}
87−else
88−{
89− string errorMessage = await response.Content.ReadAsStringAsync();
90− Console.WriteLine($"API error: {response.StatusCode}, {errorMessage}");
91−
92− // Try to parse JSON error
93− try
94− {
95− using JsonDocument errorDoc = JsonDocument.Parse(errorMessage);
96− if (errorDoc.RootElement.TryGetProperty("error", out JsonElement errorElement))
97− {
98− // Extract detailed error
99− return null;
100− }
101− }
102− catch (JsonException)
103− {
104− // Fallback to raw message
105− }
106−
107− // Show user-friendly error
108− ErrorPopupManager.ShowError("Translation Error", errorMessage);
109−
110− return null;
111−}
112−```
11339  
114−### Exception Handling
115−```csharp
116−try
117−{
118− var response = await _httpClient.PostAsync(url, content);
119− // Process response
120−}
121−catch (Exception ex)
122−{
123− Console.WriteLine($"Service error: {ex.Message}");
124− LogManager.Instance.LogError("Translation failed", ex);
125−
126− ErrorPopupManager.ShowError("Translation Error", ex.Message);
127−
128− return null;
129−}
130−```
40+## Security
13141  
132−## Service Factory Pattern
42+- Never commit sensitive data, including credentials, tokens, passwords, private keys, cookies, customer data, personal data, or machine-specific authentication material.
43+- If an AI assistant needs authentication data or other secrets for local work, use `agents_secret.md` for those notes.
44+- `agents_secret.md` must stay ignored by git and must not be committed.
45+- Do not put secrets in commit messages, logs, issue text, pull request descriptions, generated docs, or other tracked files.
46+- Configuration logging must pass key names through `ConfigManager.IsSensitiveConfigKey`; never print raw secret values in startup or harness output.
47+- Before committing, review staged changes for accidental secrets.
13348  
134−### TranslationServiceFactory
135−```csharp
136−public static class TranslationServiceFactory
137−{
138− public static ITranslationService CreateService(string serviceName)
139− {
140− return serviceName switch
141− {
142− "Gemini" => new GeminiTranslationService(),
143− "ChatGPT" => new ChatGptTranslationService(),
144− "Ollama" => new OllamaTranslationService(),
145− "Google Translate" => new GoogleTranslateService(),
146− "llama.cpp" => new LlamaCppTranslationService(),
147− _ => throw new ArgumentException($"Unknown service: {serviceName}")
148− };
149− }
150−}
151−```
49+## Git
15250  
153−## OCR Service Integration
154− 
155−### Windows OCR Pattern
156−```csharp
157−public class WindowsOCRManager
158−{
159− public async Task<List<TextObject>> ProcessImageAsync(Bitmap bitmap)
160− {
161− return await Task.Run(() =>
162− {
163− // Convert bitmap to Windows bitmap
164− // Process with Windows OCR API
165− // Return TextObject list
166− });
167− }
168−}
169−```
170− 
171−### Google Vision OCR Pattern
172−```csharp
173−public class GoogleVisionOCRService
174−{
175− public async Task<List<TextObject>> ProcessImageAsync(Bitmap bitmap)
176− {
177− // Convert bitmap to base64
178− // Send to Google Vision API
179− // Parse response and return TextObject list
180− }
181−}
182−```
183− 
184−### Python OCR Service Pattern (HTTP-based)
185−```csharp
186−// Services are managed via PythonServicesManager
187−// Discover services on startup
188−PythonServicesManager.Instance.DiscoverServices();
189− 
190−// Get service by name
191−var service = PythonServicesManager.Instance.GetServiceByName("EasyOCR");
192− 
193−// Check if service is running
194−if (!service.IsRunning)
195−{
196− bool isRunning = await service.CheckIsRunningAsync();
197− if (!isRunning)
198− {
199− // Show error dialog or start service
200− await service.StartAsync(showWindow: false);
201− }
202−}
203− 
204−// Process image with HTTP service
205−private async Task<string?> ProcessImageWithHttpServiceAsync(byte[] imageBytes, string serviceName, string language)
206−{
207− var service = PythonServicesManager.Instance.GetServiceByName(serviceName);
208− if (service == null) return null;
209−
210− // Build URL with query parameters
211− string langParam = MapLanguageForService(language);
212− string url = $"{service.ServerUrl}:{service.Port}/process?lang={langParam}&char_level=true";
213−
214− // Add service-specific parameters (e.g., MangaOCR)
215− if (serviceName == "MangaOCR")
216− {
217− url += $"&min_region_width={minWidth}&min_region_height={minHeight}&overlap_allowed_percent={overlapPercent}";
218− }
219−
220− // Add PaddleOCR-specific parameters
221− if (serviceName == "PaddleOCR")
222− {
223− bool useAngleCls = ConfigManager.Instance.GetBoolValue(ConfigManager.PADDLE_OCR_USE_ANGLE_CLS, false);
224− url += $"&use_angle_cls={useAngleCls.ToString().ToLower()}";
225− }
226−
227− // Send binary image data
228− var content = new ByteArrayContent(imageBytes);
229− content.Headers.ContentType = new MediaTypeHeaderValue("application/octet-stream");
230−
231− using var request = new HttpRequestMessage(HttpMethod.Post, url);
232− request.Content = content;
233− request.Headers.ConnectionClose = false; // Enable HTTP keep-alive
234− var response = await _httpClient.SendAsync(request);
235−
236− if (!response.IsSuccessStatusCode)
237− {
238− service.MarkAsNotRunning();
239− return null;
240− }
241−
242− return await response.Content.ReadAsStringAsync();
243−}
244−```
245− 
246−## Text-to-Speech Integration
247− 
248−### TTS Service Pattern
249−```csharp
250−public interface ITTSService
251−{
252− Task<byte[]?> SynthesizeAsync(string text, string language);
253−}
254− 
255−public class GoogleTTSService : ITTSService
256−{
257− public async Task<byte[]?> SynthesizeAsync(string text, string language)
258− {
259− var url = $"https://texttospeech.googleapis.com/v1/text:synthesize";
260− // Build request and get audio bytes
261− return audioBytes;
262− }
263−}
264−```
265− 
266−## Audio Processing
267− 
268−### NAudio Integration
269−```csharp
270−using NAudio.Wave;
271− 
272−public class AudioPlaybackManager
273−{
274− private WaveOutEvent? _waveOut;
275−
276− public void PlayAudio(byte[] audioData)
277− {
278− _waveOut?.Stop();
279− _waveOut?.Dispose();
280−
281− using var ms = new MemoryStream(audioData);
282− using var reader = new Mp3FileReader(ms);
283−
284− _waveOut = new WaveOutEvent();
285− _waveOut.Init(reader);
286− _waveOut.Play();
287− }
288−}
289−```
290− 
291−### Audio Preloading
292−```csharp
293−public class AudioPreloadService
294−{
295− // Preload audio for source and target languages
296− public async Task PreloadAudioAsync(string text, string language, string service)
297− {
298− // Generate audio and cache it
299− // Used for faster playback later
300− }
301−}
302−```
303− 
304−## Real-time Audio Streaming
305− 
306−### WebSocket Pattern
307−```csharp
308−public class OpenAIRealtimeAudioService
309−{
310− private ClientWebSocket? _webSocket;
311−
312− public async Task ConnectAsync()
313− {
314− _webSocket = new ClientWebSocket();
315− await _webSocket.ConnectAsync(new Uri("wss://api.openai.com/v1/realtime"), CancellationToken.None);
316−
317− // Start receiving loop
318− _ = Task.Run(ReceiveLoop);
319− }
320−
321− private async Task ReceiveLoop()
322− {
323− var buffer = new byte[4096];
324− while (_webSocket?.State == WebSocketState.Open)
325− {
326− var result = await _webSocket.ReceiveAsync(new ArraySegment<byte>(buffer), CancellationToken.None);
327− ProcessMessage(buffer, result.Count);
328− }
329− }
330−}
331−```
332− 
333−## Service Configuration
334− 
335−### Python OCR Service Configuration
336−Python OCR services are configured via `service_config.txt` files in each service directory:
337− 
338−```csharp
339−// Service discovery reads service_config.txt
340−// Format: key|value|
341−// Fields: service_name, venv_name, port, description, version, author, github_url, local_only, server_url
342− 
343−// C# PythonService.ParseFromConfig() reads the config file
344−var service = PythonService.ParseFromConfig(serviceDirectory);
345− 
346−// Service properties available:
347−// - service.ServiceName (from service_name)
348−// - service.VenvName (from venv_name)
349−// - service.Port (from port)
350−// - service.Description, Version, Author, GithubUrl, etc.
351−```
352− 
353−### Translation Service Configuration
354−Translation services read their configuration from ConfigManager:
355− 
356−```csharp
357−public class GeminiTranslationService
358−{
359− private readonly string _apiKey;
360− private readonly string _model;
361− private readonly string _prompt;
362−
363− public GeminiTranslationService()
364− {
365− _apiKey = ConfigManager.Instance.GetGeminiApiKey();
366− _model = ConfigManager.Instance.GetGeminiModel();
367− _prompt = ConfigManager.Instance.GetGeminiPrompt();
368− }
369−}
370−```
371− 
372−## Retry Logic
373− 
374−### Exponential Backoff Pattern
375−```csharp
376−public async Task<TranslationResult?> TranslateWithRetryAsync(string text, int maxRetries = 3)
377−{
378− for (int attempt = 0; attempt < maxRetries; attempt++)
379− {
380− try
381− {
382− return await TranslateAsync(text);
383− }
384− catch (Exception ex)
385− {
386− if (attempt == maxRetries - 1)
387− {
388− throw;
389− }
390−
391− int delay = (int)Math.Pow(2, attempt) * 1000; // Exponential backoff
392− await Task.Delay(delay);
393− }
394− }
395−
396− return null;
397−}
398−```
399− 
400−## Timeout Handling
401− 
402−### Request Timeout
403−```csharp
404−private static readonly HttpClient _httpClient = new HttpClient
405−{
406− Timeout = TimeSpan.FromSeconds(30)
407−};
408−```
409− 
410−### Cancellation Token
411−```csharp
412−using var cts = new CancellationTokenSource(TimeSpan.FromSeconds(30));
413−var response = await _httpClient.PostAsync(url, content, cts.Token);
414−```
415− 
416−## JSON Serialization
417− 
418−### System.Text.Json Pattern
419−```csharp
420−using System.Text.Json;
421− 
422−var options = new JsonSerializerOptions
423−{
424− PropertyNamingPolicy = JsonNamingPolicy.CamelCase,
425− WriteIndented = false
426−};
427− 
428−var json = JsonSerializer.Serialize(requestObject, options);
429−var responseObject = JsonSerializer.Deserialize<ResponseType>(jsonString, options);
430−```
431− 
432−## Service Health Checks
433− 
434−### Connection Testing
435−```csharp
436−public async Task<bool> TestConnectionAsync()
437−{
438− try
439− {
440− var testRequest = new { text = "test" };
441− var result = await TranslateAsync("test", "en", "");
442− return result != null;
443− }
444− catch
445− {
446− return false;
447− }
448−}
449−```
450− 
451−### Python Service Health Check
452−```csharp
453−public async Task<bool> CheckServiceHealthAsync(PythonService service)
454−{
455− try
456− {
457− var url = $"{service.ServerUrl}:{service.Port}/health";
458− var response = await _httpClient.GetAsync(url);
459− return response.IsSuccessStatusCode;
460− }
461− catch
462− {
463− return false;
464− }
465−}
466−```
467− 
468−## Service Discovery
469− 
470−### Python Service Discovery
471−```csharp
472−// Automatically discovers services in app/services/
473−PythonServicesManager.Instance.DiscoverServices();
474− 
475−// Get all discovered services
476−var services = PythonServicesManager.Instance.GetAllServices();
477− 
478−// Get service by name
479−var easyOCR = PythonServicesManager.Instance.GetServiceByName("EasyOCR");
480−```
481− 
482−## Service Lifecycle Management
483− 
484−### Starting Services
485−```csharp
486−public async Task<bool> StartServiceAsync(PythonService service, bool showWindow = false)
487−{
488− if (service.IsRunning)
489− {
490− return true;
491− }
492−
493− // Start Python server process
494− // Wait for health check
495− // Mark as running
496−}
497−```
498− 
499−### Stopping Services
500−```csharp
501−public async Task StopServiceAsync(PythonService service)
502−{
503− if (!service.IsRunning)
504− {
505− return;
506− }
507−
508− // Send shutdown request
509− // Wait for process to exit
510− // Mark as not running
511−}
512−```
51+- Never add OpenAI/Codex/Claude etc as a co-author on git commits.
52+- NEVER `git commit` unless explicitly told to commit.
53+- NEVER `git push` unless explicitly told to push. "Commit" means commit
54+ locally only; committing is not permission to push.
51355  
RuleStack

Built by

Kynth Studio

Directory

Configs
Stacks
Compare formats
Diff two configs
Best AGENTS.md examples

Formats

AGENTS.md
CLAUDE.md
Cursor rules
Copilot instructions

Reference

Read API
Corpus health
Privacy Policy
Terms

RuleStack

RuleStack

Built by

Kynth Studio

Directory

Configs
Stacks
Compare formats
Diff two configs
Best AGENTS.md examples

Formats

AGENTS.md
CLAUDE.md
Cursor rules
Copilot instructions

Reference

Read API
Corpus health
Privacy Policy
Terms

RuleStack

RuleStack

Built by

Kynth Studio

Directory

Configs
Stacks
Compare formats
Diff two configs
Best AGENTS.md examples

Formats

AGENTS.md
CLAUDE.md
Cursor rules
Copilot instructions

Reference

Read API
Corpus health
Privacy Policy
Terms

RuleStack