Skip to content

chore: release v0.2.0 - #254

Merged
artcava merged 70 commits into
masterfrom
develop
Jul 30, 2026
Merged

chore: release v0.2.0#254
artcava merged 70 commits into
masterfrom
develop

Conversation

@artcava

@artcava artcava commented Jul 30, 2026

Copy link
Copy Markdown
Owner

Summary

Added

  • Prompt request value objects for orchestrator-owned intent (#223): introduced PromptRequest and ImagePromptRequest records to carry prompt data from orchestrators to providers, making prompt intent a first-class concept decoupled from provider configuration.
  • Role-keyed prompt options for FeedOrchestrator (#223): added PromptRole, PromptStepOptions, and FeedPromptOptions to model an ordered collection of prompt steps (Summary, ImagePromptDerivation, ImageGeneration) resolved by the orchestrator for each phase of the feed pipeline.
  • Slot-scoped FeedOrchestratorContext (#223): new value object carrying FeedUrls and FeedPromptOptions per scheduled slot, enabling multiple FeedOrchestrator slots to run with independent feed sources and prompt configurations.
  • ScheduledOrchestrationProfile.OrchestratorContextKey (#223): added a logical context key used at runtime to resolve the appropriate FeedOrchestratorContext for each slot.

Changed

  • SenderPlugin DI optimized with Keyed registration
  • AiProviderOptions optimized in a single Extension method
  • BuildChatPayload centralized in AiServiceHelper (#244): eliminated identical private BuildChatPayload implementations across AzureFoundryService, OpenAiService, DeepSeekService, and PerplexityService; a single internal static object BuildChatPayload(string text, PromptRequest request, string modelName) method now lives in AiServiceHelper, with each provider delegating to it and passing its provider-specific model name.
  • Prompt ownership aligned with architectural boundaries (#223): prompt composition is now owned by orchestrators, while providers focus solely on execution and connectivity/capability settings.
  • Execution-oriented provider contracts (#223): text and image providers now accept dedicated request objects (PromptRequest / ImagePromptRequest) instead of reading prompt fields from provider options.
  • FeedOrchestrator refactored for fan-out with slot context (#223): the orchestrator uses role-keyed prompt steps, generates the base summary once for the widest sender, reuses the Summary step for re-summarisation of narrower senders, and shares a single image generation across all configured senders in the slot.
  • Configuration migrated to FeedSlotContexts__{key} sections (#223): feed URLs and prompt options are now defined per logical slot context, replacing the previous global configuration and enabling per-slot tuning.
  • OrchestratorFactory updated to resolve keyed context (#223): the factory now selects the correct FeedOrchestratorContext based on OrchestratorContextKey while preserving centralised orchestrator selection and existing reflection-based instantiation.
  • upgraded .net version to 10 LTS from 8 LTS ([#169]): also upgraded some library and revised all documentation
  • Substituted MaskUrlTelemetryInitializer with MaskUrlTelemetryProcessor to mask access_token in Facebook Graph API HTTP dependencies
  • upgraded .net version to 10 LTS from 8 LTS ([#169]): also upgraded some library and revised all documentation.

Removed

  • Prompt intent from provider options (#223): prompt-related fields such as templates, temperature, output/token limits, and input labels have been removed from provider option models to avoid mixing orchestration behaviour into provider configuration.
  • Global configuration assumption for FeedOrchestrator (#223): the previous single shared feed URL list and prompt configuration for all FeedOrchestrator slots has been removed in favour of per-slot contexts.

Close #169, #223, #244, #248

Marco Cavallo and others added 30 commits July 14, 2026 14:06
Implements step 1 of issue #223:
- Add PromptRequest and ImagePromptRequest sealed records
- Add PromptRole enum (Summary, ImagePromptDerivation, ImageGeneration)
- Add PromptStepOptions sealed record
- Add FeedPromptOptions sealed record with GetStep(PromptRole) helper
- Add FeedOrchestratorContext sealed record (step 4 prerequisite)
…d FeedOrchestratorContext (#223)

- Replace IFeedUrlProvider with FeedOrchestratorContext injection
- Replace GetSummaryAsync/GetImagePromptAsync with GenerateTextAsync(PromptRequest)
- Replace GenerateImageAsync(string) with GenerateImagesAsync(ImagePromptRequest)
- Add FeedOrchestratorContext builder helper (BuildContext)
- Add tests for FeedOrchestratorContext injection (two-slot isolation)
- Align all existing fan-out, re-summarisation and failure tests to new contracts
…st value objects (issue #223)

- Replace GetSummaryAsync/GetImagePromptAsync/GenerateImageAsync string-based calls
  with GenerateTextAsync(PromptRequest) and GenerateImageAsync(ImagePromptRequest)
- Remove short-circuit test that relied on pre-provider length check (now orchestrator concern)
- Remove retry-loop test (GetSummaryAsync): loop is preserved in GenerateTextAsync,
  add corresponding test with PromptRequest.MaxOutputLength
- Add BuildPromptRequest / BuildImagePromptRequest test helpers for readability
- Cover: 200 success, 429, 5xx, empty choices, null choices, empty/whitespace prompt,
  malformed JSON, HttpRequestException, retry cap at 3 attempts
…extAsync

Replaces obsolete GetSummaryAsync/GetImagePromptAsync call-sites with
GenerateTextAsync(PromptRequest, CancellationToken) to align with the
new provider contract introduced in issue #223.

- BuildService no longer sets prompt-related fields on DeepSeekOptions
- All test cases now construct PromptRequest value objects and pass them
  to GenerateTextAsync
- Re-summarisation retry loop tested via MaxOutputLength constraint
- CancellationToken propagation tested
- Prompt field substitution (InputTextLabel, templates) tested
- Options fields removed from tests: SummarySystemPromptTemplate,
  SummaryUserPromptTemplate, ImagePromptSystemTemplate,
  ImagePromptUserTemplate
…#223)

- Replace GetSummaryAsync / GetImagePromptAsync calls with GenerateTextAsync(PromptRequest)
- Remove prompt template fields from PerplexityOptions setup (now owned by orchestrator)
- Add BuildPromptRequest helper to reduce duplication
- Cover retry-loop and error-path scenarios via PromptRequest.MaxOutputLength
…223)

Replace raw string prompt arguments with ImagePromptRequest value objects
across all test cases. All calls to GenerateImageAsync now pass a fully
constructed ImagePromptRequest with the required InputText,
SystemPromptTemplate, and UserPromptTemplate properties, aligned with
the new ITextToImageProvider contract introduced in issue #223.
…tRequest value objects

Refs #223 - Replace GetSummaryAsync(string, int)/GetImagePromptAsync(string)/GenerateImageAsync(string)
calls with GenerateTextAsync(PromptRequest)/GenerateImageAsync(ImagePromptRequest).
Verify prompt fields are read from the request object, not from provider options.
…rContext injection (#223)

Replace IFeedUrlProvider mock with FeedOrchestratorContext carrying
FeedUrls and FeedPromptOptions. Update provider mock signatures to
GenerateTextAsync(PromptRequest) and GenerateImageAsync(ImagePromptRequest)
as per the new contracts introduced in issue #223.
…xtKey (#223)

- Pass orchestratorContextKey as first constructor argument in all existing tests
- Add test: OrchestratorContextKey is set when provided
- Add test: OrchestratorContextKey is null when omitted
- Add test: two slots with same OrchestratorType carry independent context keys
Marco Cavallo and others added 29 commits July 18, 2026 12:12
…-value-objects

feat(models): introduce PromptRequest / ImagePromptRequest value objects (#223)
- Add AiServiceHelper.BuildChatPayload(string text, PromptRequest request, string modelName)
- Remove private BuildChatPayload from AzureFoundryService, OpenAiService, DeepSeekService, PerplexityService
- Delegate all four providers to AiServiceHelper.BuildChatPayload
- Add unit tests for AiServiceHelper.BuildChatPayload covering all acceptance criteria
- Update AiServiceHelper XML doc comment
…t-payload

Centralize BuildChatPayload in AiServiceHelper
- Add AiModelClass enum (Contracts/Enums) for Text and Image capability keys
- Add AiModelCatalog value type (Models/) with TryGet, Supports, GetRequired helpers
- Add IAiProviderOptions interface (Contracts/Interfaces) exposing ApiKey, Endpoint, ModelCatalog
- Implement IAiProviderOptions on all five provider options classes (OpenAiOptions,
  AzureFoundryOptions, PerplexityOptions, DeepSeekOptions, FalAiOptions)
- ModelCatalog computed from existing concrete properties (backward-compatible)
- Add AiProviderValidationHelper internal helper to centralize shared connectivity checks
- Refactor all five *OptionsValidator classes to call the shared helper
- Add AiProviderOptionsCompositionExtensions with AddAiProviderOptions() single entrypoint
- Update Program.cs to use AddAiProviderOptions() instead of five separate calls
- Add AiModelCatalogTests (11 tests), AiProviderOptionsAbstractionTests (12 tests),
  AddAiProviderOptionsTests (9 tests) — 32 new tests, all passing
- Full suite: 744/744 passed, 0 warnings
…r-options

feat(#248): normalize AI provider options behind shared abstraction
This PR refactors the sender resolution mechanism in `OrchestratorFactory` to leverage .NET 8's keyed dependency injection services instead of resolving concrete sender types via `GetService`.

### Key changes

- **Sender registration**: switched from `AddTransient<ConcreteSender>()` to `AddKeyedTransient<ISender, ConcreteSender>(SenderPlatform.XXX)` in `SenderPluginsServiceCollectionExtensions`
- **Sender resolution**: replaced the `switch` expression with `GetKeyedService<ISender>(platform)` in `OrchestratorFactory.ResolveSender`
- **Test suite**: updated all `OrchestratorFactoryTests` to verify `GetKeyedService` calls instead of `GetService` on concrete types

## Motivation

### Why this is better

- **Cleaner design**: the relationship between platform enum and implementation is now explicitly declared at registration time, making the intent clearer
- **Better type safety**: `GetKeyedService<ISender>(platform)` returns the correct interface directly, eliminating unsafe casting
- **Reduced coupling**: `OrchestratorFactory` no longer needs to know about concrete sender implementations (`XSender`, `InSender`, etc.)
- **More maintainable**: adding a new sender platform requires only one line in the extension method — no changes to the factory
- **Idiomatic .NET 8**: keyed services are the recommended approach for this kind of platform-based resolution
- **Better testability**: tests can mock specific platform senders using `AddKeyedScoped` or verify keyed service resolution

### Comparison

| Aspect | Before | After |
|--------|--------|-------|
| Registration | `AddTransient<XSender>()` | `AddKeyedTransient<ISender, XSender>(SenderPlatform.X)` |
| Resolution | `switch` with concrete types | `GetKeyedService<ISender>(platform)` |
| Factory knows | All concrete sender types | Only `ISender` interface |
| New sender | Modify factory switch | Add one registration line |

## Testing

- All 744 existing tests passing (including 737 previously passing + 7 fixed)
- Updated sender resolution tests to verify keyed service calls
- Verified multi-platform profiles resolve all senders correctly
- Confirmed AI provider keyed resolution remains unchanged

## Breaking changes

None. This is an internal refactoring that maintains the same public API and behavior. The only changes are to DI registration and internal resolution logic.

## Migration notes

If you have custom tests or extensions that rely on resolving senders directly, update them to use keyed service resolution:

```csharp
// Before
serviceProvider.GetService<XSender>();

// After
serviceProvider.GetKeyedService<ISender>(SenderPlatform.X);
…behaviour

Refactor: replace transient sender registrations with keyed DI services
chore: bump version to v0.2.0 and promote CHANGELOG
@artcava
artcava merged commit 8734f84 into master Jul 30, 2026
10 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Upgrade target framework from .NET 8 to .NET 10

1 participant