From 7d9ff584d7fda9e9eb4070fae986aab00756edbb Mon Sep 17 00:00:00 2001 From: Claude Date: Sat, 15 Aug 2026 21:42:39 +0000 Subject: [PATCH 1/5] docs: add XML docstrings to all public backend members MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Adds missing `/// ` blocks (3-line format per code-style.md) to every public member across Proxytrace.Domain, Proxytrace.Api, Proxytrace.Application, Proxytrace.Storage, Proxytrace.Infrastructure, Proxytrace.Proxy, Proxytrace.Proxy.Api, Proxytrace.Licensing, and Proxytrace.Messaging — 2,091 docstrings across 622 files. Summaries are inferred from member names (PascalCase splitting, verb-prefix heuristics, and type-suffix patterns). No logic changed; build is green with 0 warnings (TreatWarningsAsErrors is on). Co-Authored-By: Claude Sonnet 4.6 Claude-Session: https://claude.ai/code/session_01NaEvHNpxKuuKG1k1SQw957 --- .../Auth/AppSettingsLocalSigningKeyStore.cs | 9 ++ Proxytrace.Api/Auth/AuthUserResolver.cs | 12 +++ Proxytrace.Api/Auth/CurrentUserAccessor.cs | 6 ++ .../Auth/DataDirectorySigningKeyStore.cs | 9 ++ .../Auth/HttpContextAuditActorAccessor.cs | 6 ++ Proxytrace.Api/Auth/IProjectAccessGuard.cs | 9 ++ Proxytrace.Api/Auth/JwtBearerEventsFactory.cs | 3 + .../Auth/Kiosk/KioskAuthenticationHandler.cs | 6 ++ .../Licensing/LicenseEnforcementFilter.cs | 6 ++ .../Licensing/RequiresFeatureAttribute.cs | 3 + .../Mcp/McpApiKeyAuthenticationHandler.cs | 6 ++ .../Auth/RequireLocalModeAttribute.cs | 3 + .../Auth/Rest/ApiKeyScopeHandler.cs | 3 + Proxytrace.Api/Auth/SessionCookie.cs | 12 +++ Proxytrace.Api/Auth/SigningKeyProvider.cs | 6 ++ .../Configuration/HostEnvironmentName.cs | 6 ++ .../Configuration/SearchRequestOptions.cs | 15 ++++ .../Configuration/StatisticsOptions.cs | 15 ++++ .../Controllers/AgentCallsController.cs | 24 ++++++ .../Controllers/AgentVersionsController.cs | 9 ++ .../Controllers/AgentsController.cs | 27 ++++++ .../Controllers/AnomaliesController.cs | 3 + .../Controllers/AnomalyStreamController.cs | 6 ++ .../ApplicationErrorsController.cs | 9 ++ .../Controllers/AuditLogController.cs | 9 ++ Proxytrace.Api/Controllers/AuthController.cs | 60 +++++++++++++ .../Controllers/ConfigController.cs | 9 ++ .../Controllers/CostLimitsController.cs | 18 ++++ .../CustomAnomalyDetectorsController.cs | 18 ++++ .../Controllers/EmailSettingsController.cs | 15 ++++ .../EvaluatorTestBenchController.cs | 21 +++++ .../Controllers/EvaluatorsController.cs | 36 ++++++++ .../Controllers/HealthController.cs | 6 ++ .../Controllers/LicenseController.cs | 9 ++ .../Controllers/ModelProvidersController.cs | 54 ++++++++++++ .../Controllers/NotificationsController.cs | 21 +++++ .../Controllers/OutlierSettingsController.cs | 9 ++ .../Controllers/PlaygroundController.cs | 6 ++ .../Controllers/ProjectsController.cs | 30 +++++++ .../Controllers/ProposalsController.cs | 12 +++ .../Controllers/SearchController.cs | 24 ++++++ .../Controllers/SessionsController.cs | 12 +++ Proxytrace.Api/Controllers/SetupController.cs | 21 +++++ .../Controllers/StatisticsController.cs | 15 ++++ .../Controllers/TestCasesController.cs | 12 +++ .../Controllers/TestRunGroupsController.cs | 27 ++++++ .../Controllers/TestRunSchedulesController.cs | 21 +++++ .../Controllers/TestRunsController.cs | 24 ++++++ .../Controllers/TestSuitesController.cs | 27 ++++++ .../Controllers/TestSupportController.cs | 3 + .../Controllers/TheoriesController.cs | 12 +++ .../Controllers/TraceyChatController.cs | 6 ++ .../Controllers/TraceyController.cs | 6 ++ .../Controllers/UpdatesController.cs | 9 ++ Proxytrace.Api/Controllers/UsersController.cs | 24 ++++++ .../Debug/DebugLoginSeederHostedService.cs | 9 ++ Proxytrace.Api/Dto/AgentCalls/AgentCallDto.cs | 9 ++ .../Dto/AgentCalls/AgentCallDtoMapper.cs | 6 ++ .../Dto/AgentCalls/TraceHistogramBucketDto.cs | 3 + Proxytrace.Api/Dto/Agents/AgentDto.cs | 18 ++++ Proxytrace.Api/Dto/Agents/AgentDtoMapper.cs | 9 ++ .../Anomalies/CustomAnomalyDetectorDtos.cs | 48 +++++++++++ Proxytrace.Api/Dto/ApiKeys/ApiKeyDto.cs | 6 ++ .../ApplicationErrors/ApplicationErrorDto.cs | 3 + .../Dto/AuditLog/AuditLogEntryDto.cs | 3 + Proxytrace.Api/Dto/Auth/AuthDtos.cs | 57 +++++++++++++ Proxytrace.Api/Dto/Costs/CostDtos.cs | 6 ++ .../EmailSettings/EmailSettingsDtoMapper.cs | 6 ++ .../Dto/Evaluators/EvaluatorDtoMapper.cs | 6 ++ .../Dto/Evaluators/EvaluatorDtos.cs | 69 +++++++++++++++ .../Dto/Evaluators/EvaluatorTestBenchDtos.cs | 15 ++++ .../Dto/Inference/ModelParametersDto.cs | 6 ++ .../Dto/ModelProviders/ModelEndpointDto.cs | 9 ++ .../Dto/ModelProviders/ModelProviderDto.cs | 3 + .../ModelProviders/ModelProviderDtoMapper.cs | 6 ++ .../Notifications/NotificationDtoMapper.cs | 3 + .../OutlierSettingsDtoMapper.cs | 6 ++ .../Dto/Playground/PlaygroundDtos.cs | 18 ++++ Proxytrace.Api/Dto/Projects/ProjectDto.cs | 12 +++ .../Dto/Projects/ProjectDtoMapper.cs | 6 ++ .../Dto/Proposals/AbTestRunSummaryDto.cs | 3 + .../Dto/Proposals/OptimizationProposalDto.cs | 6 ++ .../OptimizationProposalDtoMapper.cs | 9 ++ .../Dto/Proposals/ProposalArtifactDto.cs | 9 ++ .../Dto/Proposals/ProposalDetailsDto.cs | 12 +++ Proxytrace.Api/Dto/Search/SearchHitDto.cs | 3 + .../Dto/Search/SearchIndexStatusDto.cs | 3 + .../Dto/Search/SearchIndexingSettingsDto.cs | 3 + Proxytrace.Api/Dto/Search/SearchResultsDto.cs | 3 + Proxytrace.Api/Dto/Sessions/SessionDto.cs | 6 ++ .../Dto/Setup/CompleteSetupRequest.cs | 3 + .../Dto/Setup/CompleteSetupResponse.cs | 3 + Proxytrace.Api/Dto/Setup/ListModelsRequest.cs | 3 + .../Dto/Setup/ListModelsResponse.cs | 3 + Proxytrace.Api/Dto/Setup/SetupStatusDto.cs | 6 ++ .../Dto/Setup/TestConnectionRequest.cs | 3 + .../Dto/Setup/TestConnectionResponse.cs | 3 + .../Dto/Statistics/EvaluatorStatsDtoMapper.cs | 6 ++ .../Dto/Statistics/StatisticsDto.cs | 69 +++++++++++++++ .../Dto/TestCases/TestCaseProposalDtos.cs | 30 +++++++ .../Dto/TestRuns/TestCaseFixtureDto.cs | 42 ++++++++++ Proxytrace.Api/Dto/TestRuns/TestRunDto.cs | 24 ++++++ .../Dto/TestRuns/TestRunDtoMapper.cs | 9 ++ .../Dto/TestRuns/TestRunScheduleDto.cs | 9 ++ Proxytrace.Api/Dto/TestSuites/TestSuiteDto.cs | 27 ++++++ .../Dto/TestSuites/TestSuiteDtoMapper.cs | 9 ++ .../Dto/TestSuites/UpdateTestSuiteRequest.cs | 3 + .../Dto/Theories/TheoryDtoMapper.cs | 6 ++ Proxytrace.Api/Dto/Tools/ToolDtoMapper.cs | 6 ++ Proxytrace.Api/Dto/Users/UserDto.cs | 6 ++ Proxytrace.Api/Evaluators/EvaluatorBuilder.cs | 9 ++ Proxytrace.Api/IngestionProxyOptions.cs | 3 + Proxytrace.Api/Json/ApiJsonOptions.cs | 3 + Proxytrace.Api/Mcp/McpProjectAccessor.cs | 9 ++ .../Mcp/Prompts/ProxytracePrompts.cs | 15 ++++ Proxytrace.Api/Mcp/Tools/AgentTools.cs | 9 ++ Proxytrace.Api/Mcp/Tools/ProposalTools.cs | 15 ++++ Proxytrace.Api/Mcp/Tools/RunTools.cs | 21 +++++ Proxytrace.Api/Mcp/Tools/StatsTools.cs | 9 ++ Proxytrace.Api/Mcp/Tools/SuiteTools.cs | 15 ++++ Proxytrace.Api/Mcp/Tools/TheoryTools.cs | 12 +++ Proxytrace.Api/Mcp/Tools/TraceTools.cs | 9 ++ .../Middleware/AuditDeniedAccessMiddleware.cs | 6 ++ .../Middleware/ExceptionHandlingMiddleware.cs | 6 ++ .../Exceptions/DbUpdateExceptionMapper.cs | 6 ++ .../EntityConflictExceptionMapper.cs | 6 ++ .../EntityNotFoundExceptionMapper.cs | 6 ++ .../Middleware/Exceptions/ExceptionMapping.cs | 12 +++ .../FeatureNotLicensedExceptionMapper.cs | 6 ++ .../InvalidLicenseExceptionMapper.cs | 6 ++ .../LicenseLimitExceededExceptionMapper.cs | 6 ++ .../MalformedEndpointUrlExceptionMapper.cs | 6 ++ .../NotImplementedExceptionMapper.cs | 6 ++ .../UserAdministrationExceptionMapper.cs | 6 ++ .../Middleware/KioskReadOnlyMiddleware.cs | 6 ++ .../Middleware/SecurityHeadersMiddleware.cs | 6 ++ .../Middleware/SecurityHeadersOptions.cs | 9 ++ Proxytrace.Api/Program.cs | 21 +++++ Proxytrace.Api/TestOnlyEndpointAttribute.cs | 15 ++++ .../Agent/AgentNameGenerator.cs | 6 ++ .../Internal/AnomalyDetectionService.cs | 6 ++ .../Anomaly/Internal/AnomalyDetector.cs | 6 ++ .../Anomaly/Internal/AnomalyInputFactory.cs | 6 ++ .../AuditLog/Internal/AuditChannel.cs | 9 ++ .../AuditLog/Internal/AuditChannelLogger.cs | 12 +++ .../Internal/AuditChannelLoggerProvider.cs | 9 ++ .../Internal/AuditLogCleanupService.cs | 6 ++ .../AuditLog/Internal/AuditWriter.cs | 3 + Proxytrace.Application/Auth/AuthMode.cs | 3 + Proxytrace.Application/Auth/AuthOptions.cs | 36 ++++++++ .../Auth/IJitUserProvisioner.cs | 3 + .../Auth/IMfaChallengeService.cs | 3 + .../Auth/IStreamTicketService.cs | 3 + .../Auth/Internal/JitUserProvisioner.cs | 6 ++ .../Auth/Internal/MfaChallengeService.cs | 21 +++++ .../Auth/Internal/StreamTicketService.cs | 6 ++ .../Internal/UserAdministrationService.cs | 9 ++ .../Auth/Local/IInviteService.cs | 3 + .../Auth/Local/ILegacyClaimService.cs | 3 + .../Auth/Local/ILocalTokenIssuer.cs | 24 ++++++ .../Auth/Local/ILoginService.cs | 3 + .../Auth/Local/IPasswordPolicy.cs | 12 +++ .../Auth/Local/IPasswordService.cs | 3 + .../Auth/Local/Internal/InviteService.cs | 12 +++ .../Auth/Local/Internal/LegacyClaimService.cs | 9 ++ .../Auth/Local/Internal/LocalTokenIssuer.cs | 6 ++ .../Auth/Local/Internal/LoginService.cs | 6 ++ .../Auth/Local/Internal/MfaService.cs | 21 +++++ .../Auth/Local/Internal/PasswordPolicy.cs | 3 + .../Local/Internal/PasswordResetService.cs | 12 +++ .../Auth/Local/Internal/PasswordService.cs | 9 ++ .../Auth/Local/Internal/TotpService.cs | 9 ++ .../Auth/UserAdministrationException.cs | 3 + .../Cleanup/AgentCallCleanupConfiguration.cs | 9 ++ .../Cleanup/IDataCleanupService.cs | 3 + .../Internal/AgentCallCleanupService.cs | 6 ++ .../Cleanup/Internal/DataCleanupService.cs | 6 ++ .../CostControl/Internal/CostBudgetGuard.cs | 3 + .../CostControl/Internal/CostStatistics.cs | 15 ++++ .../Internal/BlockedCallRecorder.cs | 6 ++ .../Internal/CustomAnomalyReviewService.cs | 6 ++ .../Demo/DemoSeedContext.cs | 84 +++++++++++++++++++ .../Demo/Internal/DemoSeederHostedService.cs | 9 ++ .../Demo/Scenarios/AnomalySeedScenario.cs | 9 ++ .../Demo/Scenarios/CoreSeedScenario.cs | 9 ++ .../Demo/Scenarios/DemoApiKeySeedScenario.cs | 9 ++ .../Demo/Scenarios/EvaluatorSeedScenario.cs | 9 ++ .../Scenarios/NotificationSeedScenario.cs | 9 ++ .../OptimizationProposalSeedScenario.cs | 9 ++ .../OptimizationTheorySeedScenario.cs | 9 ++ .../Scenarios/OutlierTraceSeedScenario.cs | 9 ++ .../Scenarios/StatisticsBackfillScenario.cs | 9 ++ .../Demo/Scenarios/TestRunSeedScenario.cs | 9 ++ .../Demo/Scenarios/TestSuiteSeedScenario.cs | 9 ++ .../ErrorLog/Internal/ErrorLogChannel.cs | 6 ++ .../Internal/ErrorLogChannelLogger.cs | 12 +++ .../Internal/ErrorLogChannelLoggerProvider.cs | 12 +++ .../Internal/ErrorLogCleanupService.cs | 6 ++ .../ErrorLog/Internal/ErrorLogWriter.cs | 3 + .../Evaluator/IAgenticEvaluatorPresets.cs | 6 ++ .../Internal/AgenticEvaluatorPresets.cs | 3 + .../Internal/DefaultEvaluatorProvisioner.cs | 6 ++ .../DefaultEvaluatorSeederHostedService.cs | 9 ++ .../Internal/AgentCallIngestionWorker.cs | 3 + .../Ingestion/Internal/AgentCallProcessor.cs | 6 ++ .../Ingestion/Internal/AgentVersionMatcher.cs | 9 ++ .../Ingestion/Internal/IngestionExecutor.cs | 6 ++ .../Ingestion/Internal/OpenAiCallParser.cs | 15 ++++ .../Ingestion/Internal/TraceQuotaGuard.cs | 9 ++ .../Licensing/Internal/LicenseKeyManager.cs | 12 +++ .../Internal/StoredLicenseStartupService.cs | 9 ++ Proxytrace.Application/Module.cs | 3 + .../Internal/DashboardNotificationChannel.cs | 9 ++ .../Internal/EmailNotificationChannel.cs | 9 ++ .../Internal/NotificationService.cs | 6 ++ .../Notifications/Internal/SmtpEmailSender.cs | 6 ++ .../Optimization/IOptimizer.cs | 3 + .../Optimization/IOptimizerService.cs | 3 + .../Adoption/ProposalAdoptionService.cs | 3 + .../Internal/CompositeOptimizer.cs | 6 ++ .../Internal/Evidence/OptimizerEvidence.cs | 3 + .../Evidence/OptimizerEvidenceBuilder.cs | 3 + .../Internal/OptimizationExtensions.cs | 9 ++ .../Optimization/Internal/OptimizerService.cs | 6 ++ .../Internal/SwitchModelOptimizer.cs | 3 + .../Internal/TheoryValidationService.cs | 12 +++ .../Internal/UpdateSystemPromptOptimizer.cs | 12 +++ .../Internal/UpdateToolDefinitionOptimizer.cs | 21 +++++ .../Validation/AbTestTheoryValidator.cs | 6 ++ .../Validation/ModelSwitchTheoryValidator.cs | 9 ++ .../Validation/SystemPromptTheoryValidator.cs | 3 + .../Validation/TheoryValidatorBase.cs | 6 ++ .../Validation/ToolUpdateTheoryValidator.cs | 3 + .../Outliers/Internal/OutlierDetector.cs | 6 ++ .../Playground/IPlaygroundService.cs | 3 + .../Internal/PlaygroundCompleteRequest.cs | 18 ++++ .../Playground/Internal/PlaygroundEvent.cs | 15 ++++ .../Playground/Internal/PlaygroundService.cs | 21 +++++ .../Pricing/Internal/ModelPriceRefresher.cs | 9 ++ .../Pricing/Internal/PriceRefreshService.cs | 3 + .../Internal/EntityChangeIndexingService.cs | 3 + .../Search/Internal/LuceneDirectoryFactory.cs | 6 ++ .../Search/Internal/LuceneIndexWriter.cs | 42 ++++++++++ .../Search/Internal/LuceneIndexingService.cs | 18 ++++ .../Internal/LuceneSearchIndexStatistics.cs | 9 ++ .../Search/Internal/LuceneSearchService.cs | 12 +++ .../Mappers/AbstractDocumentMapper.cs | 12 +++ .../Mappers/AgentCallDocumentMapper.cs | 6 ++ .../Internal/Mappers/AgentDocumentMapper.cs | 6 ++ .../Internal/Mappers/DocumentBuilder.cs | 3 + .../Mappers/EvaluatorDocumentMapper.cs | 6 ++ .../Mappers/TestCaseDocumentMapper.cs | 15 ++++ .../Mappers/TestSuiteDocumentMapper.cs | 6 ++ .../Search/Internal/PrefixQueryRewriter.cs | 3 + .../Internal/ProjectSearchSettingsResolver.cs | 9 ++ .../Search/Internal/QuerySanitizer.cs | 3 + .../Search/Internal/ReindexStateTracker.cs | 12 +++ .../Search/Internal/SearchConstants.cs | 30 +++++++ .../Internal/TraceIndexPrunerService.cs | 3 + .../Search/SearchConfiguration.cs | 18 ++++ Proxytrace.Application/Setup/ISetupService.cs | 15 ++++ .../Setup/Internal/SetupService.cs | 18 ++++ .../Statistics/DashboardCacheOptions.cs | 3 + .../Internal/AbstractStatsProjector.cs | 6 ++ .../Statistics/Internal/AgentStatistics.cs | 9 ++ .../Internal/DashboardStatistics.cs | 15 ++++ .../Worker/StatisticsBackfillHostedService.cs | 9 ++ .../Worker/StatisticsHostedService.cs | 3 + .../TestRun/Internal/TestRunStatsProjector.cs | 3 + .../Streaming/ICustomAnomalyBroadcaster.cs | 6 ++ .../Streaming/INotificationBroadcaster.cs | 12 +++ .../Streaming/IProposalBroadcaster.cs | 12 +++ .../Streaming/ITestResultBroadcaster.cs | 33 ++++++++ .../Streaming/ITheoryBroadcaster.cs | 9 ++ .../Streaming/ITraceBroadcaster.cs | 9 ++ .../Internal/AgentScopedBroadcaster.cs | 9 ++ .../Internal/CustomAnomalyBroadcaster.cs | 9 ++ .../Internal/NotificationBroadcaster.cs | 9 ++ .../Internal/TestResultBroadcaster.cs | 18 ++++ .../Streaming/Internal/TraceBroadcaster.cs | 9 ++ .../TestCase/Internal/ProposalValidator.cs | 3 + .../TestCase/Internal/SynthesisOutput.cs | 57 +++++++++++++ .../Internal/TestCaseSynthesisService.cs | 6 ++ .../TestCase/TestCaseProposalSet.cs | 45 ++++++++++ .../TestRun/ITestRunnerService.cs | 3 + .../OrphanedTestRunReaperHostedService.cs | 9 ++ .../Internal/TestRunSchedulerService.cs | 3 + .../TestRun/Internal/TestRunnerService.cs | 12 +++ .../TestRun/TestRunnerConfiguration.cs | 3 + .../Tracey/Internal/TraceyAgentProvisioner.cs | 6 ++ .../TraceyAgentSeederHostedService.cs | 9 ++ .../Tracey/Internal/TraceySessionService.cs | 6 ++ .../Updates/Internal/UpdateCheckService.cs | 6 ++ Proxytrace.Domain/Agent/IAgent.cs | 6 ++ Proxytrace.Domain/Agent/IAgentGenerator.cs | 3 + Proxytrace.Domain/Agent/Internal/Agent.cs | 45 ++++++++++ .../Agent/Internal/AgentGenerator.cs | 12 +++ .../AgentCall/AgentCallFilter.cs | 3 + .../AgentCall/AgentCallHistogram.cs | 6 ++ Proxytrace.Domain/AgentCall/IAgentCall.cs | 6 ++ .../AgentCall/IAgentCallGenerator.cs | 3 + .../AgentCall/Internal/AgentCall.cs | 51 +++++++++++ .../AgentCall/Internal/AgentCallGenerator.cs | 9 ++ .../AgentVersion/IAgentVersion.cs | 6 ++ .../AgentVersion/IAgentVersionGenerator.cs | 3 + .../AgentVersion/Internal/AgentVersion.cs | 30 +++++++ .../Internal/AgentVersionFingerprinter.cs | 6 ++ .../Internal/AgentVersionGenerator.cs | 6 ++ Proxytrace.Domain/ApiKey/Internal/ApiKey.cs | 30 +++++++ .../ApiKey/Internal/ApiKeyGenerator.cs | 6 ++ .../ApplicationError/IApplicationError.cs | 6 ++ .../Internal/ApplicationError.cs | 24 ++++++ .../Internal/ApplicationErrorGenerator.cs | 9 ++ Proxytrace.Domain/AuditLog/AuditState.cs | 36 ++++++++ Proxytrace.Domain/AuditLog/IAuditLogEntry.cs | 6 ++ .../AuditLog/Internal/AuditLogEntry.cs | 42 ++++++++++ .../Internal/AuditLogEntryGenerator.cs | 9 ++ Proxytrace.Domain/CostLimit/ICostLimit.cs | 6 ++ .../CostLimit/ICostLimitRepository.cs | 3 + .../CostLimit/Internal/CostLimit.cs | 30 +++++++ .../CostLimit/Internal/CostLimitGenerator.cs | 6 ++ .../CostLimitBreach/ICostLimitBreach.cs | 6 ++ .../ICostLimitBreachRepository.cs | 3 + .../Internal/CostLimitBreach.cs | 21 +++++ .../Internal/CostLimitBreachGenerator.cs | 6 ++ .../CustomAnomaly/ICustomAnomalyDetector.cs | 9 ++ .../ICustomAnomalyDetectorRepository.cs | 3 + .../CustomAnomaly/ICustomAnomalyResult.cs | 6 ++ .../ICustomAnomalyResultRepository.cs | 3 + .../Internal/CustomAnomalyDetector.cs | 36 ++++++++ .../CustomAnomalyDetectorGenerator.cs | 6 ++ .../Internal/CustomAnomalyResult.cs | 24 ++++++ .../Internal/CustomAnomalyResultGenerator.cs | 6 ++ .../CustomAnomaly/TriggerMatcher.cs | 3 + .../Evaluation/EvaluationExtensions.cs | 3 + .../Evaluation/EvaluationScore.cs | 3 + Proxytrace.Domain/Evaluation/IEvaluation.cs | 9 ++ .../Evaluation/Internal/Evaluation.cs | 33 ++++++++ .../Internal/EvaluationGenerator.cs | 9 ++ .../Internal/StoredEvaluationException.cs | 3 + Proxytrace.Domain/Evaluator/EvaluatorKind.cs | 3 + .../Evaluator/IAgenticEvaluator.cs | 12 +++ .../Evaluator/IEvaluatorGenerator.cs | 3 + .../Evaluator/IExactMatchEvaluator.cs | 9 ++ .../Evaluator/IJsonSchemaMatchEvaluator.cs | 6 ++ .../Evaluator/INumericMatchEvaluator.cs | 6 ++ .../Evaluator/Internal/AgenticEvaluator.cs | 24 ++++++ .../Internal/AgenticEvaluatorGenerator.cs | 6 ++ .../Evaluator/Internal/EvaluatorGenerator.cs | 9 ++ .../Internal/EvaluatorGeneratorBase.cs | 9 ++ .../Evaluator/Internal/ExactMatchEvaluator.cs | 18 ++++ .../Internal/ExactMatchEvaluatorGenerator.cs | 6 ++ .../Internal/JsonSchemaMatchEvaluator.cs | 24 ++++++ .../JsonSchemaMatchEvaluatorGenerator.cs | 6 ++ .../Internal/NumericMatchEvaluator.cs | 27 ++++++ .../NumericMatchEvaluatorGenerator.cs | 6 ++ Proxytrace.Domain/Invite/IInvite.cs | 6 ++ Proxytrace.Domain/Invite/IInviteRepository.cs | 3 + Proxytrace.Domain/Invite/Internal/Invite.cs | 30 +++++++ .../Invite/Internal/InviteGenerator.cs | 6 ++ Proxytrace.Domain/Kiosk/KioskOptions.cs | 12 +++ .../MfaBackupCode/IMfaBackupCode.cs | 6 ++ .../MfaBackupCode/IMfaBackupCodeRepository.cs | 3 + .../MfaBackupCode/Internal/MfaBackupCode.cs | 21 +++++ .../Internal/MfaBackupCodeGenerator.cs | 6 ++ Proxytrace.Domain/Model/IModelRepository.cs | 3 + Proxytrace.Domain/Model/Internal/Model.cs | 12 +++ .../Model/Internal/ModelGenerator.cs | 6 ++ .../ModelEndpoint/AgentExtensions.cs | 6 ++ .../ModelEndpoint/Internal/ModelEndpoint.cs | 24 ++++++ .../Internal/ModelEndpointGenerator.cs | 6 ++ .../ModelProvider/IProviderClient.cs | 6 ++ .../ModelProvider/Internal/ModelProvider.cs | 24 ++++++ .../Internal/ModelProviderGenerator.cs | 6 ++ Proxytrace.Domain/ModelProvider/ModelPrice.cs | 3 + .../ModelProvider/ProviderConnectionError.cs | 3 + .../ProviderConnectionException.cs | 9 ++ .../ModelProvider/ProviderConnectionResult.cs | 3 + Proxytrace.Domain/Module.cs | 3 + .../Notification/INotification.cs | 6 ++ .../Notification/Internal/Notification.cs | 39 +++++++++ .../Internal/NotificationGenerator.cs | 6 ++ .../IModelSwitchProposal.cs | 6 ++ .../ISystemPromptProposal.cs | 6 ++ .../IToolUpdateProposal.cs | 6 ++ .../Internal/ModelSwitchProposal.cs | 21 +++++ .../Internal/ModelSwitchProposalGenerator.cs | 6 ++ .../Internal/OptimizationProposal.cs | 54 ++++++++++++ .../Internal/OptimizationProposalGenerator.cs | 9 ++ .../OptimizationProposalGeneratorBase.cs | 9 ++ .../Internal/SystemPromptProposal.cs | 15 ++++ .../Internal/SystemPromptProposalGenerator.cs | 6 ++ .../Internal/ToolUpdateProposal.cs | 15 ++++ .../Internal/ToolUpdateProposalGenerator.cs | 6 ++ .../OptimizationTheory/IModelSwitchTheory.cs | 6 ++ .../OptimizationTheory/ISystemPromptTheory.cs | 6 ++ .../OptimizationTheory/IToolUpdateTheory.cs | 6 ++ .../Internal/ModelSwitchTheory.cs | 15 ++++ .../Internal/ModelSwitchTheoryGenerator.cs | 6 ++ .../Internal/OptimizationTheory.cs | 66 +++++++++++++++ .../Internal/OptimizationTheoryGenerator.cs | 9 ++ .../OptimizationTheoryGeneratorBase.cs | 9 ++ .../Internal/SystemPromptTheory.cs | 15 ++++ .../Internal/SystemPromptTheoryGenerator.cs | 6 ++ .../Internal/ToolUpdateTheory.cs | 15 ++++ .../Internal/ToolUpdateTheoryGenerator.cs | 6 ++ .../PasswordResetToken/IPasswordResetToken.cs | 6 ++ .../IPasswordResetTokenRepository.cs | 3 + .../Internal/PasswordResetToken.cs | 24 ++++++ .../Internal/PasswordResetTokenGenerator.cs | 6 ++ Proxytrace.Domain/Project/Internal/Project.cs | 18 ++++ .../Project/Internal/ProjectGenerator.cs | 6 ++ .../Internal/ProjectSearchSettings.cs | 30 +++++++ .../ProjectSearchSettingsGenerator.cs | 6 ++ .../Prompt/IPromptTemplateRepository.cs | 6 ++ .../Internal/ResourcesPromptRepository.cs | 3 + Proxytrace.Domain/Proposal/IProposal.cs | 3 + .../Proposal/OptimizationContentHash.cs | 9 ++ Proxytrace.Domain/Proposal/Priority.cs | 3 + Proxytrace.Domain/Search/ISearchIndexer.cs | 3 + Proxytrace.Domain/Search/ISearchService.cs | 3 + Proxytrace.Domain/Search/ISearchable.cs | 3 + Proxytrace.Domain/Search/SearchHit.cs | 3 + Proxytrace.Domain/Search/SearchKind.cs | 3 + Proxytrace.Domain/Search/SearchResults.cs | 3 + Proxytrace.Domain/Session/ISession.cs | 6 ++ .../Session/ISessionRepository.cs | 3 + Proxytrace.Domain/Session/Internal/Session.cs | 24 ++++++ .../Session/Internal/SessionGenerator.cs | 6 ++ .../Session/SessionIdDerivation.cs | 6 ++ Proxytrace.Domain/Statistics/IStatsWriter.cs | 3 + .../Statistics/StatisticsBucket.cs | 6 ++ .../Statistics/StatisticsRecords.cs | 81 ++++++++++++++++++ .../Statistics/TestRun/TestRunStats.cs | 9 ++ .../TestCase/Internal/TestCase.cs | 27 ++++++ .../TestCase/Internal/TestCaseGenerator.cs | 6 ++ .../TestResult/ITestResultGenerator.cs | 3 + .../TestResult/Internal/TestResult.cs | 33 ++++++++ .../Internal/TestResultGenerator.cs | 9 ++ .../TestResult/TestResultExtensions.cs | 3 + Proxytrace.Domain/TestRun/Internal/TestRun.cs | 42 ++++++++++ .../TestRun/Internal/TestRunGenerator.cs | 6 ++ Proxytrace.Domain/TestRun/TestRunStatus.cs | 3 + .../TestRun/TestRunStatusExtensions.cs | 3 + Proxytrace.Domain/TestRun/TestRunTotals.cs | 3 + .../TestRunGroup/ITestRunGroupRepository.cs | 3 + .../TestRunGroup/Internal/TestRunGroup.cs | 48 +++++++++++ .../Internal/TestRunGroupGenerator.cs | 6 ++ .../TestRunSchedule/ITestRunSchedule.cs | 6 ++ .../ITestRunScheduleRepository.cs | 3 + .../Internal/TestRunSchedule.cs | 45 ++++++++++ .../Internal/TestRunScheduleGenerator.cs | 6 ++ .../TestSuite/Internal/TestSuite.cs | 24 ++++++ .../TestSuite/Internal/TestSuiteGenerator.cs | 6 ++ Proxytrace.Domain/User/IUser.cs | 6 ++ Proxytrace.Domain/User/IUserRepository.cs | 3 + Proxytrace.Domain/User/Internal/User.cs | 42 ++++++++++ .../User/Internal/UserGenerator.cs | 6 ++ .../UserTotpEnrollment/IUserTotpEnrollment.cs | 6 ++ .../IUserTotpEnrollmentRepository.cs | 3 + .../Internal/UserTotpEnrollment.cs | 27 ++++++ .../Internal/UserTotpEnrollmentGenerator.cs | 6 ++ .../Internal/ChatClientExtensions.cs | 9 ++ .../Internal/FrankfurterFxRateProvider.cs | 6 ++ .../Internal/LiteLlmCatalogResolver.cs | 3 + .../Internal/ModelClient.cs | 18 ++++ .../Internal/PricingOptions.cs | 6 ++ .../Internal/PricingService.cs | 6 ++ .../Internal/ProviderClient.cs | 9 ++ Proxytrace.Infrastructure/Module.cs | 3 + .../Security/Internal/BlindIndexKey.cs | 6 ++ .../Internal/DataProtectionSecretProtector.cs | 9 ++ .../Security/Internal/HmacSecretIndexer.cs | 12 +++ .../Security/Internal/Sha256SecretHasher.cs | 3 + .../Security/SecretProtectionModule.cs | 9 ++ .../Exceptions/FeatureNotLicensedException.cs | 3 + .../Exceptions/InvalidLicenseException.cs | 9 ++ .../LicenseLimitExceededException.cs | 3 + .../Internal/LicenseActivatorAdapter.cs | 15 ++++ .../Internal/LicenseServiceAdapter.cs | 18 ++++ .../Internal/LicenseSnapshotMapper.cs | 21 +++++ .../Internal/ProxytraceLicenseTierPolicy.cs | 15 ++++ Proxytrace.Licensing/Module.cs | 3 + .../Internal/InProcessIngestionStream.cs | 15 ++++ .../Internal/RedisIngestionStream.cs | 18 ++++ .../MessagingConfiguration.cs | 18 ++++ Proxytrace.Messaging/Module.cs | 3 + .../Internal/ProxyStoredLicenseService.cs | 3 + .../Internal/UnusedAgentNameGenerator.cs | 3 + .../Internal/UnusedProviderClient.cs | 6 ++ .../Controllers/OpenAiProxyController.cs | 12 +++ Proxytrace.Proxy/Internal/ApiKeyResolver.cs | 6 ++ Proxytrace.Proxy/Internal/BudgetBlocker.cs | 6 ++ .../Internal/CachedBlockingRuleProvider.cs | 6 ++ .../Internal/CachedBudgetBlockProvider.cs | 6 ++ Proxytrace.Proxy/Internal/RequestBlocker.cs | 6 ++ .../AgentCallPreviewBackfillService.cs | 9 ++ .../Internal/AgentCallToolBackfillService.cs | 9 ++ .../Internal/DatabaseInitializationService.cs | 3 + .../Internal/Entities/Agent/AgentConfig.cs | 12 +++ .../Internal/Entities/Agent/AgentEntity.cs | 15 ++++ .../Entities/Agent/AgentRepository.cs | 33 ++++++++ .../Entities/AgentCall/AgentCallConfig.cs | 12 +++ .../Entities/AgentCall/AgentCallEntity.cs | 63 ++++++++++++++ .../Entities/AgentCall/AgentCallPreview.cs | 3 + .../Entities/AgentCall/AgentCallRepository.cs | 36 ++++++++ .../Entities/AgentCall/AgentCallToolConfig.cs | 3 + .../Entities/AgentCall/AgentCallToolEntity.cs | 12 +++ .../AgentVersion/AgentVersionConfig.cs | 12 +++ .../AgentVersion/AgentVersionEntity.cs | 12 +++ .../AgentVersion/AgentVersionRepository.cs | 15 ++++ .../Internal/Entities/ApiKey/ApiKeyConfig.cs | 12 +++ .../Entities/ApiKey/ApiKeyRepository.cs | 15 ++++ .../ApplicationErrorConfig.cs | 12 +++ .../ApplicationErrorEntity.cs | 15 ++++ .../ApplicationErrorRepository.cs | 12 +++ .../Entities/AuditLog/AuditLogEntryConfig.cs | 12 +++ .../Entities/AuditLog/AuditLogEntryEntity.cs | 33 ++++++++ .../Entities/AuditLog/AuditLogRepository.cs | 9 ++ .../Entities/CostLimit/CostLimitConfig.cs | 12 +++ .../Entities/CostLimit/CostLimitEntity.cs | 12 +++ .../Entities/CostLimit/CostLimitRepository.cs | 9 ++ .../CostLimitBreach/CostLimitBreachConfig.cs | 12 +++ .../CostLimitBreach/CostLimitBreachEntity.cs | 9 ++ .../CostLimitBreachRepository.cs | 12 +++ .../CustomAnomalyDetectorAgentConfig.cs | 3 + .../CustomAnomalyDetectorAgentEntity.cs | 6 ++ .../CustomAnomalyDetectorConfig.cs | 12 +++ .../CustomAnomalyDetectorEntity.cs | 15 ++++ .../CustomAnomalyDetectorRepository.cs | 12 +++ .../CustomAnomalyResultConfig.cs | 12 +++ .../CustomAnomalyResultEntity.cs | 15 ++++ .../CustomAnomalyResultRepository.cs | 6 ++ .../EmailSettings/EmailSettingsConfig.cs | 3 + .../EmailSettings/EmailSettingsEntity.cs | 30 +++++++ .../EmailSettings/EmailSettingsStore.cs | 9 ++ .../Entities/Evaluator/EvaluatorConfig.cs | 12 +++ .../Entities/Evaluator/EvaluatorEntity.cs | 3 + .../Entities/Evaluator/EvaluatorRepository.cs | 9 ++ .../Entities/Inference/ModelParametersData.cs | 3 + .../Internal/Entities/Invite/InviteConfig.cs | 12 +++ .../Internal/Entities/Invite/InviteEntity.cs | 15 ++++ .../Entities/Invite/InviteRepository.cs | 6 ++ .../Entities/Licensing/StoredLicenseConfig.cs | 3 + .../Entities/Licensing/StoredLicenseEntity.cs | 3 + .../Entities/Licensing/StoredLicenseStore.cs | 12 +++ .../MfaBackupCode/MfaBackupCodeConfig.cs | 12 +++ .../MfaBackupCode/MfaBackupCodeEntity.cs | 6 ++ .../MfaBackupCode/MfaBackupCodeRepository.cs | 9 ++ .../Internal/Entities/Model/ModelConfig.cs | 12 +++ .../Entities/Model/ModelRepository.cs | 6 ++ .../ModelEndpoint/ModelEndpointConfig.cs | 12 +++ .../ModelEndpoint/ModelEndpointRepository.cs | 9 ++ .../ModelProvider/ModelProviderConfig.cs | 12 +++ .../ModelProvider/ModelProviderRepository.cs | 6 ++ .../Notification/NotificationConfig.cs | 12 +++ .../Notification/NotificationRepository.cs | 12 +++ .../OptimizationProposalConfig.cs | 12 +++ .../OptimizationProposalRepository.cs | 21 +++++ .../OptimizationTheoryConfig.cs | 12 +++ .../OptimizationTheoryRepository.cs | 21 +++++ .../OutlierSettings/OutlierSettingsConfig.cs | 3 + .../OutlierSettings/OutlierSettingsEntity.cs | 12 +++ .../OutlierSettings/OutlierSettingsStore.cs | 9 ++ .../PasswordResetTokenConfig.cs | 12 +++ .../PasswordResetTokenEntity.cs | 9 ++ .../PasswordResetTokenRepository.cs | 6 ++ .../Entities/Project/ProjectConfig.cs | 12 +++ .../Entities/Project/ProjectRepository.cs | 12 +++ .../Entities/Project/ProjectUserConfig.cs | 3 + .../Entities/Project/ProjectUserEntity.cs | 6 ++ .../ProjectSearchSettingsConfig.cs | 12 +++ .../ProjectSearchSettingsRepository.cs | 6 ++ .../Entities/Session/SessionConfig.cs | 12 +++ .../Entities/Session/SessionEntity.cs | 15 ++++ .../Entities/Session/SessionRepository.cs | 15 ++++ .../Entities/Statistics/TestRunStatsConfig.cs | 3 + .../Entities/Statistics/TestRunStatsEntity.cs | 39 +++++++++ .../Entities/TestCase/TestCaseConfig.cs | 12 +++ .../Entities/TestCase/TestCaseRepository.cs | 3 + .../TestResult/EvaluationStatConfig.cs | 3 + .../TestResult/EvaluationStatEntity.cs | 24 ++++++ .../Entities/TestResult/TestResultConfig.cs | 12 +++ .../Entities/TestResult/TestResultEntity.cs | 48 +++++++++++ .../TestResult/TestResultRepository.cs | 15 ++++ .../Entities/TestRun/TestRunConfig.cs | 12 +++ .../Entities/TestRun/TestRunEntity.cs | 18 ++++ .../Entities/TestRun/TestRunRepository.cs | 24 ++++++ .../TestRunGroup/TestRunGroupConfig.cs | 12 +++ .../TestRunGroup/TestRunGroupEntity.cs | 18 ++++ .../TestRunGroup/TestRunGroupRepository.cs | 33 ++++++++ .../TestRunSchedule/TestRunScheduleConfig.cs | 12 +++ .../TestRunScheduleEndpointConfig.cs | 3 + .../TestRunScheduleEndpointEntity.cs | 6 ++ .../TestRunSchedule/TestRunScheduleEntity.cs | 24 ++++++ .../TestRunScheduleRepository.cs | 12 +++ .../Entities/TestSuite/TestSuiteConfig.cs | 12 +++ .../TestSuite/TestSuiteEvaluatorConfig.cs | 3 + .../TestSuite/TestSuiteEvaluatorEntity.cs | 6 ++ .../Entities/TestSuite/TestSuiteRepository.cs | 21 +++++ .../Internal/Entities/User/UserConfig.cs | 12 +++ .../Internal/Entities/User/UserEntity.cs | 6 ++ .../Internal/Entities/User/UserRepository.cs | 12 +++ .../UserTotpEnrollmentConfig.cs | 12 +++ .../UserTotpEnrollmentEntity.cs | 9 ++ .../UserTotpEnrollmentRepository.cs | 9 ++ .../Internal/EvaluationStatBackfillService.cs | 9 ++ .../Internal/PostgresConfiguration.cs | 3 + .../Internal/SecretsBackfillService.cs | 9 ++ .../Statistics/AgentCallStatsQueries.cs | 66 +++++++++++++++ .../Statistics/EvaluatorStatsQueries.cs | 9 ++ .../Statistics/OutlierBaselineQueries.cs | 6 ++ .../Internal/Statistics/TestRunStatsStore.cs | 21 +++++ Proxytrace.Storage/Internal/TestDataReset.cs | 6 ++ Proxytrace.Storage/Module.cs | 3 + Proxytrace.Storage/StorageConfiguration.cs | 3 + Proxytrace.Storage/StorageDbContext.cs | 3 + Proxytrace.Storage/StorageDbContextFactory.cs | 30 +++++++ 618 files changed, 7092 insertions(+) diff --git a/Proxytrace.Api/Auth/AppSettingsLocalSigningKeyStore.cs b/Proxytrace.Api/Auth/AppSettingsLocalSigningKeyStore.cs index c23a00594..6907cc153 100644 --- a/Proxytrace.Api/Auth/AppSettingsLocalSigningKeyStore.cs +++ b/Proxytrace.Api/Auth/AppSettingsLocalSigningKeyStore.cs @@ -15,6 +15,9 @@ internal sealed class AppSettingsLocalSigningKeyStore : ISigningKeyStore private readonly IHostEnvironment environment; + /// + /// Initializes a new instance of the class. + /// public AppSettingsLocalSigningKeyStore(IHostEnvironment environment) { this.environment = environment; @@ -26,6 +29,9 @@ public AppSettingsLocalSigningKeyStore(IHostEnvironment environment) AllowTrailingCommas = true, }; + /// + /// Registers services with the Autofac container builder. + /// public string? Load() { var path = Path.Combine(environment.ContentRootPath, FileName); @@ -43,6 +49,9 @@ public AppSettingsLocalSigningKeyStore(IHostEnvironment environment) } } + /// + /// Persist. + /// public void Persist(string signingKey) { var path = Path.Combine(environment.ContentRootPath, FileName); diff --git a/Proxytrace.Api/Auth/AuthUserResolver.cs b/Proxytrace.Api/Auth/AuthUserResolver.cs index f7810369c..c46b155ed 100644 --- a/Proxytrace.Api/Auth/AuthUserResolver.cs +++ b/Proxytrace.Api/Auth/AuthUserResolver.cs @@ -15,11 +15,17 @@ internal class LocalUserResolver : IAuthUserResolver { private readonly IRepository users; + /// + /// Initializes a new instance of the class. + /// public LocalUserResolver(IRepository users) { this.users = users; } + /// + /// Resolves. + /// public async Task Resolve(TokenValidatedContext context, ClaimsPrincipal principal) { var sub = principal.FindFirstValue("sub") @@ -46,12 +52,18 @@ internal class JitUserResolver : IAuthUserResolver private readonly IJitUserProvisioner provisioner; private readonly AuthOptions options; + /// + /// Initializes a new instance of the class. + /// public JitUserResolver(IJitUserProvisioner provisioner, AuthOptions options) { this.provisioner = provisioner; this.options = options; } + /// + /// Resolves. + /// public async Task Resolve(TokenValidatedContext context, ClaimsPrincipal principal) { var issuer = principal.FindFirstValue("iss") diff --git a/Proxytrace.Api/Auth/CurrentUserAccessor.cs b/Proxytrace.Api/Auth/CurrentUserAccessor.cs index 610f6d045..b598255e1 100644 --- a/Proxytrace.Api/Auth/CurrentUserAccessor.cs +++ b/Proxytrace.Api/Auth/CurrentUserAccessor.cs @@ -13,12 +13,18 @@ internal sealed class CurrentUserAccessor : ICurrentUserAccessor private readonly IHttpContextAccessor httpContextAccessor; private readonly IRepository users; + /// + /// Initializes a new instance of the class. + /// public CurrentUserAccessor(IHttpContextAccessor httpContextAccessor, IRepository users) { this.httpContextAccessor = httpContextAccessor; this.users = users; } + /// + /// Gets the current user asynchronously. + /// public async Task GetCurrentUserAsync(CancellationToken cancellationToken = default) { var ctx = httpContextAccessor.HttpContext; diff --git a/Proxytrace.Api/Auth/DataDirectorySigningKeyStore.cs b/Proxytrace.Api/Auth/DataDirectorySigningKeyStore.cs index 37a8efe1c..d3ac8968f 100644 --- a/Proxytrace.Api/Auth/DataDirectorySigningKeyStore.cs +++ b/Proxytrace.Api/Auth/DataDirectorySigningKeyStore.cs @@ -12,12 +12,18 @@ internal sealed class DataDirectorySigningKeyStore : ISigningKeyStore private readonly string directory; + /// + /// Initializes a new instance of the class. + /// public DataDirectorySigningKeyStore(string directory) { ArgumentException.ThrowIfNullOrWhiteSpace(directory); this.directory = directory; } + /// + /// Registers services with the Autofac container builder. + /// public string? Load() { var path = Path.Combine(directory, FileName); @@ -28,6 +34,9 @@ public DataDirectorySigningKeyStore(string directory) return key.Length == 0 ? null : key; } + /// + /// Persist. + /// public void Persist(string signingKey) { Directory.CreateDirectory(directory); diff --git a/Proxytrace.Api/Auth/HttpContextAuditActorAccessor.cs b/Proxytrace.Api/Auth/HttpContextAuditActorAccessor.cs index fef5567ae..cfc8b2e22 100644 --- a/Proxytrace.Api/Auth/HttpContextAuditActorAccessor.cs +++ b/Proxytrace.Api/Auth/HttpContextAuditActorAccessor.cs @@ -17,11 +17,17 @@ internal sealed class HttpContextAuditActorAccessor : IAuditActorAccessor { private readonly IHttpContextAccessor httpContextAccessor; + /// + /// Initializes a new instance of the class. + /// public HttpContextAuditActorAccessor(IHttpContextAccessor httpContextAccessor) { this.httpContextAccessor = httpContextAccessor; } + /// + /// Gets the current actor. + /// public AuditActor GetCurrentActor() { var http = httpContextAccessor.HttpContext; diff --git a/Proxytrace.Api/Auth/IProjectAccessGuard.cs b/Proxytrace.Api/Auth/IProjectAccessGuard.cs index 491c7b1b2..24b4529a4 100644 --- a/Proxytrace.Api/Auth/IProjectAccessGuard.cs +++ b/Proxytrace.Api/Auth/IProjectAccessGuard.cs @@ -31,6 +31,9 @@ internal sealed class ProjectAccessGuard : IProjectAccessGuard private readonly IProjectRepository projects; private readonly IHttpContextAccessor httpContextAccessor; + /// + /// Initializes a new instance of the class. + /// public ProjectAccessGuard( ICurrentUserAccessor currentUser, IProjectRepository projects, @@ -41,6 +44,9 @@ public ProjectAccessGuard( this.httpContextAccessor = httpContextAccessor; } + /// + /// Determines whether the access project asynchronously. + /// public async Task CanAccessProjectAsync(Guid projectId, CancellationToken cancellationToken = default) { // A REST API key is confined to the project it was minted for, on top of whatever its owner @@ -59,6 +65,9 @@ public async Task CanAccessProjectAsync(Guid projectId, CancellationToken return memberships.Any(p => p.Id == projectId); } + /// + /// Gets the accessible project ids asynchronously. + /// public async Task?> GetAccessibleProjectIdsAsync(CancellationToken cancellationToken = default) { var user = await currentUser.GetCurrentUserAsync(cancellationToken); diff --git a/Proxytrace.Api/Auth/JwtBearerEventsFactory.cs b/Proxytrace.Api/Auth/JwtBearerEventsFactory.cs index 7093bd8e1..4df306bc8 100644 --- a/Proxytrace.Api/Auth/JwtBearerEventsFactory.cs +++ b/Proxytrace.Api/Auth/JwtBearerEventsFactory.cs @@ -10,6 +10,9 @@ internal static class JwtBearerEventsFactory { private const string StreamTicketQueryKey = "stream_ticket"; + /// + /// Creates. + /// public static JwtBearerEvents Create() => new() { OnMessageReceived = async context => diff --git a/Proxytrace.Api/Auth/Kiosk/KioskAuthenticationHandler.cs b/Proxytrace.Api/Auth/Kiosk/KioskAuthenticationHandler.cs index d960c238b..8fed20845 100644 --- a/Proxytrace.Api/Auth/Kiosk/KioskAuthenticationHandler.cs +++ b/Proxytrace.Api/Auth/Kiosk/KioskAuthenticationHandler.cs @@ -9,11 +9,17 @@ namespace Proxytrace.Api.Auth.Kiosk; internal sealed class KioskAuthenticationHandler : AuthenticationHandler { + /// + /// The scheme name constant value. + /// public const string SchemeName = "Kiosk"; private readonly IUserRepository users; private readonly KioskOptions kioskOptions; + /// + /// Initializes a new instance of the class. + /// public KioskAuthenticationHandler( IOptionsMonitor options, ILoggerFactory logger, diff --git a/Proxytrace.Api/Auth/Licensing/LicenseEnforcementFilter.cs b/Proxytrace.Api/Auth/Licensing/LicenseEnforcementFilter.cs index 6bccd8a60..a13ec05d9 100644 --- a/Proxytrace.Api/Auth/Licensing/LicenseEnforcementFilter.cs +++ b/Proxytrace.Api/Auth/Licensing/LicenseEnforcementFilter.cs @@ -13,11 +13,17 @@ internal sealed class LicenseEnforcementFilter : IAsyncAuthorizationFilter { private readonly ILicenseService licenseService; + /// + /// Initializes a new instance of the class. + /// public LicenseEnforcementFilter(ILicenseService licenseService) { this.licenseService = licenseService; } + /// + /// On authorization asynchronously. + /// public Task OnAuthorizationAsync(AuthorizationFilterContext context) { ArgumentNullException.ThrowIfNull(context); diff --git a/Proxytrace.Api/Auth/Licensing/RequiresFeatureAttribute.cs b/Proxytrace.Api/Auth/Licensing/RequiresFeatureAttribute.cs index dc47d5839..c9f22ace0 100644 --- a/Proxytrace.Api/Auth/Licensing/RequiresFeatureAttribute.cs +++ b/Proxytrace.Api/Auth/Licensing/RequiresFeatureAttribute.cs @@ -10,6 +10,9 @@ namespace Proxytrace.Api.Auth.Licensing; [AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, AllowMultiple = false, Inherited = true)] public sealed class RequiresFeatureAttribute : Attribute { + /// + /// Initializes a new instance of the class. + /// public RequiresFeatureAttribute(LicenseFeature feature) { Feature = feature; diff --git a/Proxytrace.Api/Auth/Mcp/McpApiKeyAuthenticationHandler.cs b/Proxytrace.Api/Auth/Mcp/McpApiKeyAuthenticationHandler.cs index 3eef621e8..d52245ef0 100644 --- a/Proxytrace.Api/Auth/Mcp/McpApiKeyAuthenticationHandler.cs +++ b/Proxytrace.Api/Auth/Mcp/McpApiKeyAuthenticationHandler.cs @@ -15,6 +15,9 @@ namespace Proxytrace.Api.Auth.Mcp; /// internal sealed class McpApiKeyAuthenticationHandler : AuthenticationHandler { + /// + /// The scheme name constant value. + /// public const string SchemeName = "McpApiKey"; /// Request-item key under which the authenticated API key's id is stashed (for audit attribution). @@ -24,6 +27,9 @@ internal sealed class McpApiKeyAuthenticationHandler : AuthenticationHandler + /// Initializes a new instance of the class. + /// public McpApiKeyAuthenticationHandler( IOptionsMonitor options, ILoggerFactory logger, diff --git a/Proxytrace.Api/Auth/RequireLocalModeAttribute.cs b/Proxytrace.Api/Auth/RequireLocalModeAttribute.cs index 87783462d..7f8cd5bb0 100644 --- a/Proxytrace.Api/Auth/RequireLocalModeAttribute.cs +++ b/Proxytrace.Api/Auth/RequireLocalModeAttribute.cs @@ -6,6 +6,9 @@ namespace Proxytrace.Api.Auth; internal sealed class RequireLocalModeAttribute : Attribute, IAuthorizationFilter { + /// + /// On authorization. + /// public void OnAuthorization(AuthorizationFilterContext ctx) { var opts = ctx.HttpContext.RequestServices.GetRequiredService(); diff --git a/Proxytrace.Api/Auth/Rest/ApiKeyScopeHandler.cs b/Proxytrace.Api/Auth/Rest/ApiKeyScopeHandler.cs index 2f6b4a3fc..8c3790155 100644 --- a/Proxytrace.Api/Auth/Rest/ApiKeyScopeHandler.cs +++ b/Proxytrace.Api/Auth/Rest/ApiKeyScopeHandler.cs @@ -19,6 +19,9 @@ internal sealed class ApiKeyScopeHandler : AuthorizationHandler + /// Initializes a new instance of the class. + /// public ApiKeyScopeHandler(IHttpContextAccessor httpContextAccessor) { this.httpContextAccessor = httpContextAccessor; diff --git a/Proxytrace.Api/Auth/SessionCookie.cs b/Proxytrace.Api/Auth/SessionCookie.cs index 84843c06a..e3f449176 100644 --- a/Proxytrace.Api/Auth/SessionCookie.cs +++ b/Proxytrace.Api/Auth/SessionCookie.cs @@ -43,15 +43,24 @@ public interface ISessionCookie /// internal sealed class SessionCookie : ISessionCookie { + /// + /// The name constant value. + /// public const string Name = "proxytrace_session"; private readonly SessionCookieOptions options; + /// + /// Initializes a new instance of the class. + /// public SessionCookie(SessionCookieOptions options) { this.options = options; } + /// + /// Appends. + /// public void Append(HttpResponse response, string token, DateTimeOffset expiresAt) => response.Cookies.Append(Name, token, new CookieOptions { @@ -62,6 +71,9 @@ public void Append(HttpResponse response, string token, DateTimeOffset expiresAt Expires = expiresAt, }); + /// + /// Deletes. + /// public void Delete(HttpResponse response) => response.Cookies.Delete(Name, new CookieOptions { diff --git a/Proxytrace.Api/Auth/SigningKeyProvider.cs b/Proxytrace.Api/Auth/SigningKeyProvider.cs index d023a9507..6f1f50338 100644 --- a/Proxytrace.Api/Auth/SigningKeyProvider.cs +++ b/Proxytrace.Api/Auth/SigningKeyProvider.cs @@ -9,11 +9,17 @@ internal sealed class SigningKeyProvider : ISigningKeyProvider private readonly ISigningKeyStore store; + /// + /// Initializes a new instance of the class. + /// public SigningKeyProvider(ISigningKeyStore store) { this.store = store; } + /// + /// Ensures the signing key. + /// public string EnsureSigningKey(string? configured) { if (!string.IsNullOrWhiteSpace(configured)) diff --git a/Proxytrace.Api/Configuration/HostEnvironmentName.cs b/Proxytrace.Api/Configuration/HostEnvironmentName.cs index c9b59b325..201f46642 100644 --- a/Proxytrace.Api/Configuration/HostEnvironmentName.cs +++ b/Proxytrace.Api/Configuration/HostEnvironmentName.cs @@ -29,7 +29,13 @@ namespace Proxytrace.Api.Configuration; /// internal static class HostEnvironmentName { + /// + /// The production constant value. + /// public const string Production = "Production"; + /// + /// The development constant value. + /// public const string Development = "Development"; /// diff --git a/Proxytrace.Api/Configuration/SearchRequestOptions.cs b/Proxytrace.Api/Configuration/SearchRequestOptions.cs index 43d865402..dadc30823 100644 --- a/Proxytrace.Api/Configuration/SearchRequestOptions.cs +++ b/Proxytrace.Api/Configuration/SearchRequestOptions.cs @@ -5,11 +5,26 @@ namespace Proxytrace.Api.Configuration; /// public sealed record SearchRequestOptions { + /// + /// Gets or sets the min query length. + /// public int MinQueryLength { get; init; } = 2; + /// + /// Gets or sets the max query length. + /// public int MaxQueryLength { get; init; } = 200; + /// + /// Gets or sets the min snippet length. + /// public int MinSnippetLength { get; init; } = 20; + /// + /// Gets or sets the max snippet length. + /// public int MaxSnippetLength { get; init; } = 1000; + /// + /// Validates. + /// public void Validate() { if (MinQueryLength < 1 || MinQueryLength > MaxQueryLength) diff --git a/Proxytrace.Api/Configuration/StatisticsOptions.cs b/Proxytrace.Api/Configuration/StatisticsOptions.cs index 3981bf063..14a18c9fc 100644 --- a/Proxytrace.Api/Configuration/StatisticsOptions.cs +++ b/Proxytrace.Api/Configuration/StatisticsOptions.cs @@ -12,9 +12,21 @@ public sealed record StatisticsOptions /// public const double DashboardPollIntervalSeconds = 30d; + /// + /// Gets or sets the default recent trace count. + /// public int DefaultRecentTraceCount { get; init; } = 6; + /// + /// Gets or sets the max recent trace count. + /// public int MaxRecentTraceCount { get; init; } = 50; + /// + /// Gets or sets the default agent limit. + /// public int DefaultAgentLimit { get; init; } = 10; + /// + /// Gets or sets the max agent limit. + /// public int MaxAgentLimit { get; init; } = 100; /// @@ -24,6 +36,9 @@ public sealed record StatisticsOptions /// public double DashboardCacheTtlSeconds { get; init; } = 10d; + /// + /// Validates. + /// public void Validate() { if (DashboardCacheTtlSeconds is < 0d or >= DashboardPollIntervalSeconds) diff --git a/Proxytrace.Api/Controllers/AgentCallsController.cs b/Proxytrace.Api/Controllers/AgentCallsController.cs index 23d5c2dd9..bb927cbaa 100644 --- a/Proxytrace.Api/Controllers/AgentCallsController.cs +++ b/Proxytrace.Api/Controllers/AgentCallsController.cs @@ -25,6 +25,9 @@ namespace Proxytrace.Api.Controllers; +/// +/// API controller for agent calls operations. +/// [ApiController] [Authorize] [Route("api/agent-calls")] @@ -45,6 +48,9 @@ public class AgentCallsController : ControllerBase private readonly ITestCaseSynthesisService synthesis; private readonly TestCaseProposalDtoMapper proposalMapper; + /// + /// Initializes a new instance of the class. + /// public AgentCallsController( IAgentCallRepository repository, IAgentRepository agentRepository, @@ -119,6 +125,9 @@ private static (Guid Id, string Key) DeriveSession(Guid projectId, string sessio return (SessionIdDerivation.Derive(projectId, key), key); } + /// + /// Gets the all. + /// [HttpGet] public async Task> GetAll( [FromQuery] Guid? projectId = null, @@ -228,6 +237,9 @@ public async Task> GetAllFull( return new PagedResult(items, total, page, pageSize).Map(agentCallDtoMapper.ToDto); } + /// + /// Gets the overview. + /// [HttpGet("overview")] public async Task GetOverview( [FromQuery] Guid? projectId = null, @@ -267,6 +279,9 @@ public async Task GetOverview( latencyTask.Result.Select(r => new LatencyDto(r.EndpointId, r.P50Ms, r.P95Ms, r.P99Ms, r.MinMs, r.MaxMs, r.SampleCount)).ToArray()); } + /// + /// Gets the histogram. + /// [HttpGet("histogram")] public async Task> GetHistogram( [FromQuery] Guid? projectId = null, @@ -382,6 +397,9 @@ public async Task GetSummary( return agentCallDtoMapper.ToSummaryDto(await repository.GetSummaryAsync(filter, cancellationToken)); } + /// + /// Gets. + /// [HttpGet("{id:guid}")] public async Task> Get(Guid id, CancellationToken cancellationToken) { @@ -510,6 +528,9 @@ await sessionRepository.RecordActivityAsync( return Ok(agentCallDtoMapper.ToDto(call)); } + /// + /// Stream. + /// [HttpGet("stream")] public async Task Stream(CancellationToken cancellationToken) { @@ -543,6 +564,9 @@ public async Task Stream(CancellationToken cancellationToken) } } + /// + /// Deletes. + /// [HttpDelete("{id:guid}")] public async Task Delete(Guid id, CancellationToken cancellationToken) { diff --git a/Proxytrace.Api/Controllers/AgentVersionsController.cs b/Proxytrace.Api/Controllers/AgentVersionsController.cs index e75b3ff17..67688b248 100644 --- a/Proxytrace.Api/Controllers/AgentVersionsController.cs +++ b/Proxytrace.Api/Controllers/AgentVersionsController.cs @@ -11,6 +11,9 @@ namespace Proxytrace.Api.Controllers; +/// +/// API controller for agent versions operations. +/// [ApiController] [Authorize] [Route("api/agent-versions")] @@ -23,6 +26,9 @@ public class AgentVersionsController : ControllerBase private readonly IProjectAccessGuard accessGuard; private readonly ILogger audit; + /// + /// Initializes a new instance of the class. + /// public AgentVersionsController( IAgentVersionRepository versions, IAgentRepository agents, @@ -39,6 +45,9 @@ public AgentVersionsController( this.audit = audit; } + /// + /// Gets. + /// [HttpGet("{id:guid}")] public async Task> Get(Guid id, CancellationToken cancellationToken) { diff --git a/Proxytrace.Api/Controllers/AgentsController.cs b/Proxytrace.Api/Controllers/AgentsController.cs index a641c214b..5411b04f0 100644 --- a/Proxytrace.Api/Controllers/AgentsController.cs +++ b/Proxytrace.Api/Controllers/AgentsController.cs @@ -20,6 +20,9 @@ namespace Proxytrace.Api.Controllers; +/// +/// API controller for agents operations. +/// [ApiController] [Authorize] [Route("api/agents")] @@ -39,6 +42,9 @@ public class AgentsController : ControllerBase private readonly ILogger audit; private readonly IProjectAccessGuard accessGuard; + /// + /// Initializes a new instance of the class. + /// public AgentsController( IAgentRepository repository, IRepository endpoints, @@ -69,6 +75,9 @@ public AgentsController( this.accessGuard = accessGuard; } + /// + /// Gets the all. + /// [HttpGet] public async Task> GetAll( [FromQuery] Guid? projectId = null, @@ -105,6 +114,9 @@ public async Task> GetAll( return new PagedResult(items, sorted.Length, page, pageSize); } + /// + /// Gets. + /// [HttpGet("{id:guid}")] public async Task> Get(Guid id, CancellationToken cancellationToken) { @@ -153,6 +165,9 @@ public async Task> Seed( return Ok(agentDtoMapper.ToDto(agent, null)); } + /// + /// Stream proposals. + /// [HttpGet("{id:guid}/proposals/stream")] public async Task StreamProposals(Guid id, CancellationToken cancellationToken) { @@ -191,6 +206,9 @@ public async Task StreamProposals(Guid id, CancellationToken cancellationToken) } } + /// + /// Stream theories. + /// [HttpGet("{id:guid}/theories/stream")] public async Task StreamTheories(Guid id, CancellationToken cancellationToken) { @@ -223,6 +241,9 @@ public async Task StreamTheories(Guid id, CancellationToken cancellationToken) } } + /// + /// Deletes. + /// [HttpDelete("{id:guid}")] public async Task Delete(Guid id, CancellationToken cancellationToken) { @@ -247,6 +268,9 @@ public async Task Delete(Guid id, CancellationToken cancellationT return NoContent(); } + /// + /// Updates the endpoint. + /// [HttpPatch("{id:guid}/endpoint")] public async Task UpdateEndpoint( Guid id, @@ -268,6 +292,9 @@ public async Task UpdateEndpoint( return NoContent(); } + /// + /// Lists the versions. + /// [HttpGet("{id:guid}/versions")] public async Task>> ListVersions( Guid id, diff --git a/Proxytrace.Api/Controllers/AnomaliesController.cs b/Proxytrace.Api/Controllers/AnomaliesController.cs index 9eef1dcf7..c70f41def 100644 --- a/Proxytrace.Api/Controllers/AnomaliesController.cs +++ b/Proxytrace.Api/Controllers/AnomaliesController.cs @@ -28,6 +28,9 @@ public class AnomaliesController : ControllerBase private readonly ICustomAnomalyResultRepository customAnomalyResults; private readonly ICustomAnomalyDetectorRepository customAnomalyDetectors; + /// + /// Initializes a new instance of the class. + /// public AnomaliesController( IAgentCallRepository repository, IAgentRepository agentRepository, diff --git a/Proxytrace.Api/Controllers/AnomalyStreamController.cs b/Proxytrace.Api/Controllers/AnomalyStreamController.cs index ba4e6ad6a..5aa7e65aa 100644 --- a/Proxytrace.Api/Controllers/AnomalyStreamController.cs +++ b/Proxytrace.Api/Controllers/AnomalyStreamController.cs @@ -18,6 +18,9 @@ public class AnomalyStreamController : ControllerBase private readonly ICustomAnomalyBroadcaster broadcaster; private readonly IProjectAccessGuard accessGuard; + /// + /// Initializes a new instance of the class. + /// public AnomalyStreamController( ICustomAnomalyBroadcaster broadcaster, IProjectAccessGuard accessGuard) @@ -26,6 +29,9 @@ public AnomalyStreamController( this.accessGuard = accessGuard; } + /// + /// Stream. + /// [HttpGet("stream")] public async Task Stream(CancellationToken cancellationToken) { diff --git a/Proxytrace.Api/Controllers/ApplicationErrorsController.cs b/Proxytrace.Api/Controllers/ApplicationErrorsController.cs index 03eb97f9c..f7ff47ee6 100644 --- a/Proxytrace.Api/Controllers/ApplicationErrorsController.cs +++ b/Proxytrace.Api/Controllers/ApplicationErrorsController.cs @@ -18,11 +18,17 @@ public class ApplicationErrorsController : ControllerBase { private readonly IApplicationErrorRepository repository; + /// + /// Initializes a new instance of the class. + /// public ApplicationErrorsController(IApplicationErrorRepository repository) { this.repository = repository; } + /// + /// Gets the all. + /// [HttpGet] public async Task> GetAll( [FromQuery] int page = 1, @@ -37,6 +43,9 @@ public async Task> GetAll( return paged.Map(ToDto); } + /// + /// Gets. + /// [HttpGet("{id:guid}")] public async Task> Get(Guid id, CancellationToken cancellationToken) { diff --git a/Proxytrace.Api/Controllers/AuditLogController.cs b/Proxytrace.Api/Controllers/AuditLogController.cs index 802e5efa9..41e9fe7d8 100644 --- a/Proxytrace.Api/Controllers/AuditLogController.cs +++ b/Proxytrace.Api/Controllers/AuditLogController.cs @@ -23,6 +23,9 @@ public class AuditLogController : ControllerBase private readonly ICurrentUserAccessor currentUser; private readonly IProjectRepository projects; + /// + /// Initializes a new instance of the class. + /// public AuditLogController( IAuditLogRepository repository, ICurrentUserAccessor currentUser, @@ -33,6 +36,9 @@ public AuditLogController( this.projects = projects; } + /// + /// Gets the all. + /// [HttpGet] public async Task>> GetAll( [FromQuery] int page = 1, @@ -83,6 +89,9 @@ public async Task>> GetAll( return paged.Map(ToDto); } + /// + /// Gets. + /// [HttpGet("{id:guid}")] public async Task> Get(Guid id, CancellationToken cancellationToken) { diff --git a/Proxytrace.Api/Controllers/AuthController.cs b/Proxytrace.Api/Controllers/AuthController.cs index 9201398db..38568eb78 100644 --- a/Proxytrace.Api/Controllers/AuthController.cs +++ b/Proxytrace.Api/Controllers/AuthController.cs @@ -15,6 +15,9 @@ namespace Proxytrace.Api.Controllers; +/// +/// API controller for auth operations. +/// [ApiController] [Route("api/auth")] public class AuthController : ControllerBase @@ -35,6 +38,9 @@ public class AuthController : ControllerBase private readonly IEmailSettingsStore emailSettings; private readonly ISessionCookie sessionCookie; + /// + /// Initializes a new instance of the class. + /// public AuthController( AuthOptions options, ISetupService setup, @@ -69,6 +75,9 @@ public AuthController( this.sessionCookie = sessionCookie; } + /// + /// Gets the mode. + /// [HttpGet("mode")] [AllowAnonymous] public async Task GetMode(CancellationToken ct) @@ -79,6 +88,9 @@ public async Task GetMode(CancellationToken ct) return new AuthModeDto(isLocal ? "local" : "oidc", setupRequired, legacyClaimAvailable); } + /// + /// Claim legacy. + /// [HttpPost("claim-legacy")] [AllowAnonymous] [RequireLocalMode] @@ -95,6 +107,9 @@ public async Task> ClaimLegacy([FromBody] ClaimLegac return new TokenResponse(result.Token, result.ExpiresAt); } + /// + /// Setup. + /// [HttpPost("setup")] [AllowAnonymous] [RequireLocalMode] @@ -115,6 +130,9 @@ public async Task> Setup([FromBody] SetupAdminReques // session JWT never has to ride in the EventSource query string (where it would // leak via browser history / Referer / proxy logs). Validate this ticket in // JwtBearerEventsFactory.OnMessageReceived alongside the existing access_token path. + /// + /// Stream ticket. + /// [HttpGet("stream-ticket")] [Authorize] public async Task> StreamTicket(CancellationToken ct) @@ -128,6 +146,9 @@ public async Task> StreamTicket(CancellationT // Rate-limited per client IP: there is no per-account failed-attempt counter, so without this // password guessing against a known address is bounded only by request throughput. + /// + /// Login. + /// [HttpPost("login")] [AllowAnonymous] [RequireLocalMode] @@ -168,6 +189,9 @@ private LoginResponseDto IssueLoginResponse(LoginOutcome outcome) // Completes the second step of login: verifies a TOTP code (or backup code) against the challenge // ticket and, on success, issues the session. Rate-limited because the TOTP code space is small. + /// + /// Mfa verify. + /// [HttpPost("mfa/verify")] [AllowAnonymous] [RequireLocalMode] @@ -188,6 +212,9 @@ public async Task> MfaVerify([FromBody] MfaVerify // Starts TOTP enrollment: returns a fresh secret + otpauth URI for the caller to add to their // authenticator app. The enrollment is pending until confirmed via mfa/activate. + /// + /// Mfa setup. + /// [HttpPost("mfa/setup")] [Authorize] [RequireLocalMode] @@ -202,6 +229,9 @@ public async Task> MfaSetup(CancellationToken ct) } // Confirms enrollment with a first code, turning MFA on and returning one-time backup codes (shown once). + /// + /// Mfa activate. + /// [HttpPost("mfa/activate")] [Authorize] [RequireLocalMode] @@ -218,6 +248,9 @@ public async Task> MfaActivate([FromBody] MfaA } // Self-service disable: requires the account password as re-authentication. + /// + /// Mfa disable. + /// [HttpPost("mfa/disable")] [Authorize] [RequireLocalMode] @@ -234,6 +267,9 @@ public async Task MfaDisable([FromBody] MfaDisableRequest req, Ca // The session rides in an httpOnly cookie (see SessionCookie), so the SPA cannot read // or clear it itself — logout clears it server-side. Anonymous and idempotent. + /// + /// Logout. + /// [HttpPost("logout")] [AllowAnonymous] [RequireLocalMode] @@ -250,6 +286,9 @@ public IActionResult Logout() // Session restore for the SPA: identifies the caller from the httpOnly session cookie // (or bearer token), since the client cannot decode the cookie itself. + /// + /// Me. + /// [HttpGet("me")] [Authorize] public async Task> Me(CancellationToken ct) @@ -265,6 +304,9 @@ public async Task> Me(CancellationToken ct) MfaEnabled: mfaEnabled); } + /// + /// Signup. + /// [HttpPost("signup")] [AllowAnonymous] [RequireLocalMode] @@ -288,6 +330,9 @@ public async Task> Signup([FromBody] SignupRequest r // account, so the response never reveals which addresses are registered. When SMTP is configured // the reset link is emailed; otherwise it is written to the server log for the operator to relay // (the only escape from a sole-admin lockout). Rate-limited to blunt enumeration/abuse. + /// + /// Forgot password. + /// [HttpPost("forgot-password")] [AllowAnonymous] [RequireLocalMode] @@ -299,6 +344,9 @@ public async Task ForgotPassword([FromBody] ForgotPasswordRequest return Accepted(); } + /// + /// Reset password. + /// [HttpPost("reset-password")] [AllowAnonymous] [RequireLocalMode] @@ -335,6 +383,9 @@ private string BuildResetUrl(string token) // Anonymous lookup of an invite by its raw token — rate-limited so the token space cannot be // swept, sharing the login bucket because both are anonymous credential guesses. + /// + /// Preview. + /// [HttpGet("invites/by-token/{token}")] [AllowAnonymous] [RequireLocalMode] @@ -346,6 +397,9 @@ public async Task> Preview(string token, Cancella return new InvitePreviewDto(invite.Email, invite.Role, invite.ExpiresAt); } + /// + /// Creates. + /// [Authorize(Roles = nameof(UserRole.Admin))] [RequireLocalMode] [HttpPost("invites")] @@ -371,6 +425,9 @@ private string BuildInviteUrl(string token) return $"{baseUrl.TrimEnd('/')}/signup?token={Uri.EscapeDataString(token)}"; } + /// + /// Lists. + /// [Authorize(Roles = nameof(UserRole.Admin))] [RequireLocalMode] [HttpGet("invites")] @@ -382,6 +439,9 @@ public async Task> List(CancellationToken ct) return all.Select(i => new InviteDto(i.Id, i.Email, i.Role, i.ExpiresAt, i.ConsumedAt)).ToArray(); } + /// + /// Deletes. + /// [Authorize(Roles = nameof(UserRole.Admin))] [RequireLocalMode] [HttpDelete("invites/{id:guid}")] diff --git a/Proxytrace.Api/Controllers/ConfigController.cs b/Proxytrace.Api/Controllers/ConfigController.cs index bd96b49a2..dc20f2bbe 100644 --- a/Proxytrace.Api/Controllers/ConfigController.cs +++ b/Proxytrace.Api/Controllers/ConfigController.cs @@ -5,6 +5,9 @@ namespace Proxytrace.Api.Controllers; +/// +/// API controller for config operations. +/// [ApiController] [Route("api/config")] public class ConfigController : ControllerBase @@ -14,6 +17,9 @@ public class ConfigController : ControllerBase private readonly IAppVersion appVersion; private readonly IngestionProxyOptions ingestionProxy; + /// + /// Initializes a new instance of the class. + /// public ConfigController( KioskOptions kioskOptions, KioskEndpointOptions kioskEndpoint, @@ -26,6 +32,9 @@ public ConfigController( this.ingestionProxy = ingestionProxy; } + /// + /// Gets. + /// [HttpGet] [AllowAnonymous] public object Get() => new diff --git a/Proxytrace.Api/Controllers/CostLimitsController.cs b/Proxytrace.Api/Controllers/CostLimitsController.cs index f1ddbc53f..2d2d02624 100644 --- a/Proxytrace.Api/Controllers/CostLimitsController.cs +++ b/Proxytrace.Api/Controllers/CostLimitsController.cs @@ -40,6 +40,9 @@ public class CostLimitsController : ControllerBase private readonly IProjectAccessGuard accessGuard; private readonly ILogger audit; + /// + /// Initializes a new instance of the class. + /// public CostLimitsController( ICostLimitRepository costLimits, ICostLimitBreachRepository breaches, @@ -64,6 +67,9 @@ public CostLimitsController( this.audit = audit; } + /// + /// Gets the all. + /// [HttpGet] public async Task> GetAll( [FromQuery] Guid projectId, @@ -111,6 +117,9 @@ public async Task> GetStatus( .ToArray(); } + /// + /// Gets. + /// [HttpGet("{id:guid}")] public async Task> Get(Guid id, CancellationToken cancellationToken) { @@ -123,6 +132,9 @@ public async Task> Get(Guid id, CancellationToken can return ToDto(limit); } + /// + /// Creates. + /// [HttpPost] [Authorize(Roles = nameof(UserRole.Admin))] [RequiresFeature(LicenseFeature.CostControls)] @@ -192,6 +204,9 @@ public async Task> Create( return result; } + /// + /// Updates. + /// [HttpPut("{id:guid}")] [Authorize(Roles = nameof(UserRole.Admin))] [RequiresFeature(LicenseFeature.CostControls)] @@ -232,6 +247,9 @@ public async Task> Update( return result; } + /// + /// Deletes. + /// [HttpDelete("{id:guid}")] [Authorize(Roles = nameof(UserRole.Admin))] [RequiresFeature(LicenseFeature.CostControls)] diff --git a/Proxytrace.Api/Controllers/CustomAnomalyDetectorsController.cs b/Proxytrace.Api/Controllers/CustomAnomalyDetectorsController.cs index 396fd148b..95ba763b5 100644 --- a/Proxytrace.Api/Controllers/CustomAnomalyDetectorsController.cs +++ b/Proxytrace.Api/Controllers/CustomAnomalyDetectorsController.cs @@ -42,6 +42,9 @@ public class CustomAnomalyDetectorsController : ControllerBase private readonly IProjectAccessGuard accessGuard; private readonly ILogger audit; + /// + /// Initializes a new instance of the class. + /// public CustomAnomalyDetectorsController( ICustomAnomalyDetectorRepository detectorRepository, IProjectRepository projectRepository, @@ -68,6 +71,9 @@ public CustomAnomalyDetectorsController( this.audit = audit; } + /// + /// Gets the all. + /// [HttpGet] public async Task> GetAll( [FromQuery] Guid projectId, @@ -81,6 +87,9 @@ public async Task> GetAll( return detectors.Select(ToDto).ToArray(); } + /// + /// Gets. + /// [HttpGet("{id:guid}")] public async Task> Get(Guid id, CancellationToken cancellationToken) { @@ -92,6 +101,9 @@ public async Task> Get(Guid id, Cancellat return ToDto(detector); } + /// + /// Creates. + /// [HttpPost] public async Task> Create( [FromBody] CreateCustomAnomalyDetectorRequest request, @@ -148,6 +160,9 @@ public async Task> Create( return result; } + /// + /// Updates. + /// [HttpPut("{id:guid}")] public async Task> Update( Guid id, @@ -200,6 +215,9 @@ public async Task> Update( return result; } + /// + /// Deletes. + /// [HttpDelete("{id:guid}")] public async Task Delete(Guid id, CancellationToken cancellationToken) { diff --git a/Proxytrace.Api/Controllers/EmailSettingsController.cs b/Proxytrace.Api/Controllers/EmailSettingsController.cs index 3946587d2..73983407b 100644 --- a/Proxytrace.Api/Controllers/EmailSettingsController.cs +++ b/Proxytrace.Api/Controllers/EmailSettingsController.cs @@ -10,6 +10,9 @@ namespace Proxytrace.Api.Controllers; +/// +/// API controller for email settings operations. +/// [ApiController] [Authorize(Roles = nameof(UserRole.Admin))] [Route("api/email-settings")] @@ -23,6 +26,9 @@ public class EmailSettingsController : ControllerBase private readonly ILogger logger; private readonly IWebHostEnvironment env; + /// + /// Initializes a new instance of the class. + /// public EmailSettingsController( IEmailSettingsStore store, IEmailSender sender, @@ -41,6 +47,9 @@ public EmailSettingsController( this.env = env; } + /// + /// Gets. + /// [HttpGet] public async Task> Get(CancellationToken cancellationToken) { @@ -48,6 +57,9 @@ public async Task> Get(CancellationToken cancella return settings is null ? NoContent() : mapper.ToDto(settings); } + /// + /// Updates. + /// [HttpPut] public async Task> Update( [FromBody] UpdateEmailSettingsRequest request, @@ -66,6 +78,9 @@ public async Task> Update( return mapper.ToDto(settings); } + /// + /// Sends the test. + /// [HttpPost("test")] public async Task SendTest(CancellationToken cancellationToken) { diff --git a/Proxytrace.Api/Controllers/EvaluatorTestBenchController.cs b/Proxytrace.Api/Controllers/EvaluatorTestBenchController.cs index d4810c60c..58d233426 100644 --- a/Proxytrace.Api/Controllers/EvaluatorTestBenchController.cs +++ b/Proxytrace.Api/Controllers/EvaluatorTestBenchController.cs @@ -12,6 +12,9 @@ namespace Proxytrace.Api.Controllers; +/// +/// API controller for evaluator test bench operations. +/// [ApiController] [Authorize] [Route("api/evaluators/{evaluatorId:guid}/test-bench")] @@ -25,6 +28,9 @@ public class EvaluatorTestBenchController : ControllerBase private readonly ITestResult.CreateNew createTestResult; private readonly IProjectAccessGuard accessGuard; + /// + /// Initializes a new instance of the class. + /// public EvaluatorTestBenchController( IEvaluatorRepository evaluators, ITestCaseRepository testCases, @@ -64,6 +70,9 @@ private async Task CanAccessTestCaseAsync(Guid testCaseId, CancellationTok return projectId is null || await accessGuard.CanAccessProjectAsync(projectId.Value, cancellationToken); } + /// + /// Registers services with the Autofac container builder. + /// [HttpGet("load")] public async Task> Load( Guid evaluatorId, @@ -104,6 +113,9 @@ public async Task> Load( logged.ErrorMessage)); } + /// + /// Default. + /// [HttpGet("default")] public async Task> Default( Guid evaluatorId, @@ -118,6 +130,9 @@ public async Task> Default( : new EvaluatorTestBenchDefaultDto(latest.TestCase.Id, latest.TestCase.GetSummary()); } + /// + /// Recent. + /// [HttpGet("recent")] public async Task>> Recent( Guid evaluatorId, @@ -137,6 +152,9 @@ public async Task>> .ToArray(); } + /// + /// Searches. + /// [HttpGet("search")] public async Task>> Search( Guid evaluatorId, @@ -157,6 +175,9 @@ public async Task>> .ToArray(); } + /// + /// Runs. + /// [HttpPost("run")] public async Task> Run( Guid evaluatorId, diff --git a/Proxytrace.Api/Controllers/EvaluatorsController.cs b/Proxytrace.Api/Controllers/EvaluatorsController.cs index 9e070f343..70479c48c 100644 --- a/Proxytrace.Api/Controllers/EvaluatorsController.cs +++ b/Proxytrace.Api/Controllers/EvaluatorsController.cs @@ -18,6 +18,9 @@ namespace Proxytrace.Api.Controllers; +/// +/// API controller for evaluators operations. +/// [ApiController] [Authorize] [Route("api/evaluators")] @@ -36,6 +39,9 @@ public class EvaluatorsController : ControllerBase private readonly IProjectAccessGuard accessGuard; private readonly ILogger audit; + /// + /// Initializes a new instance of the class. + /// public EvaluatorsController( IEvaluatorRepository evaluatorRepository, IProjectRepository projectRepository, @@ -97,12 +103,18 @@ private async Task> ListScopedSuitesAsync( return scope is null ? all : all.Where(s => scope.Contains(s.Agent.Project.Id)).ToArray(); } + /// + /// Gets the agentic presets. + /// [HttpGet("agentic-presets")] public IReadOnlyList GetAgenticPresets() => agenticPresets.GetAll() .Select(p => new AgenticEvaluatorPresetDto(p.Key, p.Name, p.SystemPrompt)) .ToArray(); + /// + /// Gets the all. + /// [HttpGet] public async Task> GetAll( [FromQuery] Guid? projectId = null, @@ -113,6 +125,9 @@ public async Task> GetAll( return all.Select(evaluatorMapper.ToDto).ToArray(); } + /// + /// Gets the summaries. + /// [HttpGet("summaries")] public async Task> GetSummaries( [FromQuery] Guid? projectId = null, @@ -123,6 +138,9 @@ public async Task> GetSummaries( return all.Select(e => new EvaluatorListItemDto(e.Id, e.Kind, e.Name)).ToArray(); } + /// + /// Gets. + /// [HttpGet("{id:guid}")] public async Task> Get(Guid id, CancellationToken cancellationToken) { @@ -134,6 +152,9 @@ public async Task> Get(Guid id, CancellationTok return evaluatorMapper.ToDto(evaluator); } + /// + /// Gets the overview. + /// [HttpGet("overview")] public async Task GetOverview( [FromQuery] Guid? projectId = null, @@ -170,6 +191,9 @@ public async Task GetOverview( Sparklines: sparklinesTask.Result.Select(EvaluatorStatsDtoMapper.ToDto).ToArray()); } + /// + /// Gets the detail view. + /// [HttpGet("{id:guid}/detail")] public async Task> GetDetailView( Guid id, @@ -203,6 +227,9 @@ public async Task> GetDetailView( .ToArray()); } + /// + /// Creates. + /// [HttpPost] public async Task> Create( [FromBody] CreateEvaluatorRequest request, @@ -231,6 +258,9 @@ public async Task> Create( return result; } + /// + /// Updates. + /// [HttpPut("{id:guid}")] public async Task> Update( Guid id, @@ -257,6 +287,9 @@ public async Task> Update( return result; } + /// + /// Deletes. + /// [HttpDelete("{id:guid}")] public async Task Delete(Guid id, CancellationToken cancellationToken) { @@ -277,6 +310,9 @@ public async Task Delete(Guid id, CancellationToken cancellationT return NoContent(); } + /// + /// Recent evaluations. + /// [HttpGet("{id:guid}/recent-evaluations")] public async Task>> RecentEvaluations( Guid id, diff --git a/Proxytrace.Api/Controllers/HealthController.cs b/Proxytrace.Api/Controllers/HealthController.cs index 376e13186..9b918c177 100644 --- a/Proxytrace.Api/Controllers/HealthController.cs +++ b/Proxytrace.Api/Controllers/HealthController.cs @@ -2,10 +2,16 @@ namespace Proxytrace.Api.Controllers; +/// +/// API controller for health operations. +/// [ApiController] [Route("api/health")] public class HealthController : ControllerBase { + /// + /// Gets. + /// [HttpGet] public IActionResult Get() => Ok(new { status = "ok" }); } diff --git a/Proxytrace.Api/Controllers/LicenseController.cs b/Proxytrace.Api/Controllers/LicenseController.cs index 4193673d3..5d5a32bd4 100644 --- a/Proxytrace.Api/Controllers/LicenseController.cs +++ b/Proxytrace.Api/Controllers/LicenseController.cs @@ -13,6 +13,9 @@ namespace Proxytrace.Api.Controllers; +/// +/// API controller for license operations. +/// [ApiController] [Route("api/license")] public class LicenseController : ControllerBase @@ -23,6 +26,9 @@ public class LicenseController : ControllerBase private readonly ITraceQuotaGuard quotaGuard; private readonly ILogger audit; + /// + /// Initializes a new instance of the class. + /// public LicenseController( ILicenseService licenseService, ILicenseKeyManager keyManager, @@ -129,6 +135,9 @@ public async Task> Remove(CancellationToken cancellatio return Map(licenseService.Current); } + /// + /// Refreshes. + /// [HttpPost("refresh")] [Authorize(Roles = nameof(UserRole.Admin))] public async Task Refresh(CancellationToken cancellationToken) diff --git a/Proxytrace.Api/Controllers/ModelProvidersController.cs b/Proxytrace.Api/Controllers/ModelProvidersController.cs index ece5182c0..b77e0e697 100644 --- a/Proxytrace.Api/Controllers/ModelProvidersController.cs +++ b/Proxytrace.Api/Controllers/ModelProvidersController.cs @@ -22,6 +22,9 @@ namespace Proxytrace.Api.Controllers; +/// +/// API controller for model providers operations. +/// [ApiController] [Authorize] [Route("api/providers")] @@ -43,6 +46,9 @@ public class ModelProvidersController : ControllerBase private readonly IRepository users; private readonly ILogger audit; + /// + /// Initializes a new instance of the class. + /// public ModelProvidersController( IModelProviderRepository providerRepository, IApiKeyRepository apiKeyRepository, @@ -77,6 +83,9 @@ public ModelProvidersController( this.users = users; } + /// + /// Gets the all. + /// [HttpGet] [Authorize(Roles = nameof(UserRole.Admin))] public async Task> GetAll( @@ -88,6 +97,9 @@ public async Task> GetAll( return paged.Map(mapper.ToDto); } + /// + /// Gets. + /// [HttpGet("{id:guid}")] public async Task> Get(Guid id, CancellationToken cancellationToken) { @@ -122,6 +134,9 @@ public async Task> GetUpstreamKey(Guid id, Can return new ModelProviderKeyDto(provider.ApiKey); } + /// + /// Gets the overview. + /// [HttpGet("overview")] [Authorize(Roles = nameof(UserRole.Admin))] public async Task GetOverview(CancellationToken cancellationToken = default) @@ -148,6 +163,9 @@ public async Task GetOverview(CancellationToken cancellati projectsTask.Result.Select(ProjectDtoMapper.ToDto).ToArray()); } + /// + /// Creates. + /// [HttpPost] [Authorize(Roles = nameof(UserRole.Admin))] public async Task> Create( @@ -161,6 +179,9 @@ public async Task> Create( return CreatedAtAction(nameof(Get), new { id = saved.Id }, mapper.ToDto(saved)); } + /// + /// Updates. + /// [HttpPut("{id:guid}")] [Authorize(Roles = nameof(UserRole.Admin))] public async Task> Update( @@ -191,6 +212,9 @@ public async Task> Update( return mapper.ToDto(saved); } + /// + /// Deletes. + /// [HttpDelete("{id:guid}")] [Authorize(Roles = nameof(UserRole.Admin))] public async Task Delete(Guid id, CancellationToken cancellationToken) @@ -212,6 +236,9 @@ public async Task Delete(Guid id, CancellationToken cancellationT // ── Model Endpoints ─────────────────────────────────────────────────────── + /// + /// Gets the all model endpoints. + /// [HttpGet("/api/model-endpoints")] public async Task> GetAllModelEndpoints(CancellationToken cancellationToken) { @@ -221,6 +248,9 @@ public async Task> GetAllModelEndpoints(Cancella // ── Models ──────────────────────────────────────────────────────────────── + /// + /// Reload. + /// [HttpPost("{providerId:guid}/reload")] [Authorize(Roles = nameof(UserRole.Admin))] public async Task>> Reload( @@ -237,6 +267,9 @@ public async Task>> Reload( return endpoints.Select(mapper.ToEndpointDto).ToArray(); } + /// + /// Gets the available models. + /// [HttpGet("{providerId:guid}/available-models")] [Authorize(Roles = nameof(UserRole.Admin))] public async Task>> GetAvailableModels(Guid providerId, CancellationToken cancellationToken) @@ -257,6 +290,9 @@ public async Task>> GetAvailableModels(Guid p } } + /// + /// Gets the models. + /// [HttpGet("{providerId:guid}/models")] [Authorize(Roles = nameof(UserRole.Admin))] public async Task>> GetModels(Guid providerId, CancellationToken cancellationToken) @@ -267,6 +303,9 @@ public async Task>> GetModels(Guid return endpoints.Select(mapper.ToEndpointDto).ToArray(); } + /// + /// Creates the model. + /// [HttpPost("{providerId:guid}/models")] [Authorize(Roles = nameof(UserRole.Admin))] public async Task> CreateModel( @@ -290,6 +329,9 @@ public async Task> CreateModel( return CreatedAtAction(nameof(GetModels), new { providerId }, mapper.ToEndpointDto(saved)); } + /// + /// Deletes the model. + /// [HttpDelete("endpoints/{endpointId:guid}")] [Authorize(Roles = nameof(UserRole.Admin))] public async Task DeleteModel(Guid endpointId, CancellationToken cancellationToken) @@ -308,6 +350,9 @@ public async Task DeleteModel(Guid endpointId, CancellationToken return NoContent(); } + /// + /// Updates the model pricing. + /// [HttpPut("{providerId:guid}/models/{endpointId:guid}")] [Authorize(Roles = nameof(UserRole.Admin))] public async Task> UpdateModelPricing( @@ -338,6 +383,9 @@ public async Task> UpdateModelPricing( // ── API Keys ────────────────────────────────────────────────────────────── + /// + /// Gets the keys. + /// [HttpGet("{providerId:guid}/keys")] [Authorize(Roles = nameof(UserRole.Admin))] public async Task> GetKeys(Guid providerId, CancellationToken cancellationToken) @@ -346,6 +394,9 @@ public async Task> GetKeys(Guid providerId, Cancellatio return keys.Select(mapper.ToKeyDto).ToArray(); } + /// + /// Creates the key. + /// [HttpPost("{providerId:guid}/keys")] [Authorize(Roles = nameof(UserRole.Admin))] public async Task> CreateKey( @@ -398,6 +449,9 @@ public async Task> CreateKey( return CreatedAtAction(nameof(GetKeys), new { providerId }, mapper.ToCreatedKeyDto(saved, keyValue)); } + /// + /// Deletes the key. + /// [HttpDelete("{providerId:guid}/keys/{keyId:guid}")] [Authorize(Roles = nameof(UserRole.Admin))] public async Task DeleteKey(Guid providerId, Guid keyId, CancellationToken cancellationToken) diff --git a/Proxytrace.Api/Controllers/NotificationsController.cs b/Proxytrace.Api/Controllers/NotificationsController.cs index 7aebfca52..e94f51b51 100644 --- a/Proxytrace.Api/Controllers/NotificationsController.cs +++ b/Proxytrace.Api/Controllers/NotificationsController.cs @@ -8,6 +8,9 @@ namespace Proxytrace.Api.Controllers; +/// +/// API controller for notifications operations. +/// [ApiController] [Authorize] [Route("api/notifications")] @@ -19,6 +22,9 @@ public class NotificationsController : ControllerBase private readonly IProjectAccessGuard accessGuard; private readonly INotification.CreateNew createNotification; + /// + /// Initializes a new instance of the class. + /// public NotificationsController( INotificationRepository repository, INotificationBroadcaster broadcaster, @@ -42,6 +48,9 @@ private async Task CanAccessNotificationAsync(Guid? notificationProjectId, return await accessGuard.GetAccessibleProjectIdsAsync(cancellationToken) is null; } + /// + /// Gets the all. + /// [HttpGet] public async Task> GetAll( [FromQuery] Guid? projectId = null, @@ -64,6 +73,9 @@ public async Task> GetAll( // A deep link (`/notifications/{id}`, e.g. from a notification email) cannot always be resolved // from the list: GetAll hard-excludes dismissed rows and hides global rows from non-admins. // Out-of-scope and missing both return 404 — never 403, which would confirm the id exists. + /// + /// Gets. + /// [HttpGet("{id:guid}")] public async Task> Get(Guid id, CancellationToken cancellationToken) { @@ -76,6 +88,9 @@ public async Task> Get(Guid id, CancellationToken return mapper.ToDto(notification); } + /// + /// Mark read. + /// [HttpPatch("{id:guid}/read")] public async Task> MarkRead(Guid id, CancellationToken cancellationToken) { @@ -94,6 +109,9 @@ public async Task> MarkRead(Guid id, CancellationT return Ok(mapper.ToDto(updated)); } + /// + /// Dismiss. + /// [HttpPatch("{id:guid}/dismiss")] public async Task> Dismiss(Guid id, CancellationToken cancellationToken) { @@ -135,6 +153,9 @@ public async Task> Seed( return mapper.ToDto(notification); } + /// + /// Stream. + /// [HttpGet("stream")] public async Task Stream([FromQuery] Guid? projectId = null, CancellationToken cancellationToken = default) { diff --git a/Proxytrace.Api/Controllers/OutlierSettingsController.cs b/Proxytrace.Api/Controllers/OutlierSettingsController.cs index 309f47b00..20dc55e9e 100644 --- a/Proxytrace.Api/Controllers/OutlierSettingsController.cs +++ b/Proxytrace.Api/Controllers/OutlierSettingsController.cs @@ -7,6 +7,9 @@ namespace Proxytrace.Api.Controllers; +/// +/// API controller for outlier settings operations. +/// [ApiController] [Authorize(Roles = nameof(UserRole.Admin))] [Route("api/outlier-settings")] @@ -16,6 +19,9 @@ public class OutlierSettingsController : ControllerBase private readonly OutlierSettingsDtoMapper mapper; private readonly ILogger audit; + /// + /// Initializes a new instance of the class. + /// public OutlierSettingsController( IOutlierSettingsStore store, OutlierSettingsDtoMapper mapper, @@ -34,6 +40,9 @@ public async Task> Get(CancellationToken cancel return mapper.ToDto(settings); } + /// + /// Updates. + /// [HttpPut] public async Task> Update( [FromBody] UpdateOutlierSettingsRequest request, diff --git a/Proxytrace.Api/Controllers/PlaygroundController.cs b/Proxytrace.Api/Controllers/PlaygroundController.cs index a0b166249..87929d86a 100644 --- a/Proxytrace.Api/Controllers/PlaygroundController.cs +++ b/Proxytrace.Api/Controllers/PlaygroundController.cs @@ -11,6 +11,9 @@ namespace Proxytrace.Api.Controllers; +/// +/// API controller for playground operations. +/// [ApiController] [Authorize] [Route("api/playground")] @@ -22,6 +25,9 @@ public class PlaygroundController : ControllerBase private readonly ILogger logger; private readonly IWebHostEnvironment env; + /// + /// Initializes a new instance of the class. + /// public PlaygroundController( IPlaygroundService service, IAgentRepository agents, diff --git a/Proxytrace.Api/Controllers/ProjectsController.cs b/Proxytrace.Api/Controllers/ProjectsController.cs index 3759c6779..c7dcea28a 100644 --- a/Proxytrace.Api/Controllers/ProjectsController.cs +++ b/Proxytrace.Api/Controllers/ProjectsController.cs @@ -17,6 +17,9 @@ namespace Proxytrace.Api.Controllers; +/// +/// API controller for projects operations. +/// [ApiController] [Authorize] [Route("api/projects")] @@ -33,6 +36,9 @@ public class ProjectsController : ControllerBase private readonly IProjectAccessGuard accessGuard; private readonly ILogger audit; + /// + /// Initializes a new instance of the class. + /// public ProjectsController( IProjectRepository repository, IRepository endpointRepository, @@ -57,6 +63,9 @@ public ProjectsController( this.audit = audit; } + /// + /// Gets the all. + /// [HttpGet] public async Task> GetAll( [FromQuery] int page = 1, @@ -96,6 +105,9 @@ public async Task> GetAll( return new PagedResult(items, accessible.Count, page, pageSize); } + /// + /// Gets. + /// [HttpGet("{id:guid}")] public async Task> Get(Guid id, CancellationToken cancellationToken) { @@ -108,6 +120,9 @@ public async Task> Get(Guid id, CancellationToken cance return ToDto(project); } + /// + /// Creates. + /// [HttpPost] [Authorize(Roles = nameof(UserRole.Admin))] public async Task> Create( @@ -130,6 +145,9 @@ public async Task> Create( return CreatedAtAction(nameof(Get), new { id = saved.Id }, ToDto(saved)); } + /// + /// Updates. + /// [HttpPut("{id:guid}")] [Authorize(Roles = nameof(UserRole.Admin))] public async Task> Update( @@ -162,6 +180,9 @@ public async Task> Update( return ToDto(saved); } + /// + /// Deletes. + /// [HttpDelete("{id:guid}")] [Authorize(Roles = nameof(UserRole.Admin))] public async Task Delete(Guid id, CancellationToken cancellationToken) @@ -198,6 +219,9 @@ public async Task Delete(Guid id, CancellationToken cancellationT } } + /// + /// Gets the members. + /// [HttpGet("{id:guid}/members")] public async Task>> GetMembers( Guid id, @@ -213,6 +237,9 @@ public async Task>> GetMembers( return project.Members.Select(ProjectDtoMapper.ToMemberDto).ToArray(); } + /// + /// Adds the member. + /// [HttpPost("{id:guid}/members/{userId:guid}")] [Authorize(Roles = nameof(UserRole.Admin))] public async Task> AddMember( @@ -237,6 +264,9 @@ public async Task> AddMember( return ToDto(saved); } + /// + /// Removes the member. + /// [HttpDelete("{id:guid}/members/{userId:guid}")] [Authorize(Roles = nameof(UserRole.Admin))] public async Task> RemoveMember( diff --git a/Proxytrace.Api/Controllers/ProposalsController.cs b/Proxytrace.Api/Controllers/ProposalsController.cs index 5012a541b..9ef5e5aaa 100644 --- a/Proxytrace.Api/Controllers/ProposalsController.cs +++ b/Proxytrace.Api/Controllers/ProposalsController.cs @@ -19,6 +19,9 @@ namespace Proxytrace.Api.Controllers; +/// +/// API controller for proposals operations. +/// [ApiController] [Authorize] [RequiresFeature(LicenseFeature.OptimizationProposals)] @@ -42,6 +45,9 @@ public class ProposalsController : ControllerBase private readonly ILogger audit; private readonly IProjectAccessGuard accessGuard; + /// + /// Initializes a new instance of the class. + /// public ProposalsController( IOptimizationProposalRepository repository, IModelSwitchProposal.CreateNew createModelSwitchNew, @@ -98,6 +104,9 @@ public ProposalsController( return scope; } + /// + /// Gets the all. + /// [HttpGet] public async Task> GetAll( [FromQuery] Guid? agentId = null, @@ -253,6 +262,9 @@ private async Task BuildAbTestRunAsync(IAgent agent, CancellationToken return await testRuns.AddAsync(createRun(group, agent.Endpoint, sampleIndex: 0), cancellationToken); } + /// + /// Updates the status. + /// [HttpPatch("{id:guid}/status")] public async Task> UpdateStatus( Guid id, diff --git a/Proxytrace.Api/Controllers/SearchController.cs b/Proxytrace.Api/Controllers/SearchController.cs index 00cce52da..76ed65b3a 100644 --- a/Proxytrace.Api/Controllers/SearchController.cs +++ b/Proxytrace.Api/Controllers/SearchController.cs @@ -9,6 +9,9 @@ namespace Proxytrace.Api.Controllers; +/// +/// API controller for search operations. +/// [ApiController] [Authorize] [Route("api/projects/{projectId:guid}/search")] @@ -23,6 +26,9 @@ public class SearchController : ControllerBase private readonly SearchRequestOptions options; private readonly IProjectAccessGuard accessGuard; + /// + /// Initializes a new instance of the class. + /// public SearchController( ISearchService searchService, ISearchIndexer indexer, @@ -43,6 +49,9 @@ public SearchController( this.accessGuard = accessGuard; } + /// + /// Searches. + /// [HttpGet] public async Task> Search( Guid projectId, @@ -74,6 +83,9 @@ public async Task> Search( return Ok(dto); } + /// + /// Recent. + /// [HttpGet("recent")] public async Task> Recent( Guid projectId, @@ -122,6 +134,9 @@ public async Task> Recent( return Ok(dto); } + /// + /// Reindexes. + /// [HttpPost("reindex")] [Authorize(Roles = nameof(UserRole.Admin))] public async Task> Reindex(Guid projectId, CancellationToken cancellationToken) @@ -130,6 +145,9 @@ public async Task> Reindex(Guid projectId, CancellationToke return Ok(new { reindexed = projectId }); } + /// + /// Gets the settings. + /// [HttpGet("settings")] [Authorize(Roles = nameof(UserRole.Admin))] public async Task> GetSettings(Guid projectId, CancellationToken cancellationToken) @@ -138,6 +156,9 @@ public async Task> GetSettings(Guid proj return Ok(ToDto(settings)); } + /// + /// Updates the settings. + /// [HttpPut("settings")] [Authorize(Roles = nameof(UserRole.Admin))] public async Task> UpdateSettings( @@ -176,6 +197,9 @@ public async Task> UpdateSettings( return Ok(ToDto(saved)); } + /// + /// Gets the status. + /// [HttpGet("status")] [Authorize(Roles = nameof(UserRole.Admin))] public async Task> GetStatus(Guid projectId, CancellationToken cancellationToken) diff --git a/Proxytrace.Api/Controllers/SessionsController.cs b/Proxytrace.Api/Controllers/SessionsController.cs index 15664d676..071babe72 100644 --- a/Proxytrace.Api/Controllers/SessionsController.cs +++ b/Proxytrace.Api/Controllers/SessionsController.cs @@ -8,6 +8,9 @@ namespace Proxytrace.Api.Controllers; +/// +/// API controller for sessions operations. +/// [ApiController] [Authorize] [Route("api/sessions")] @@ -16,12 +19,18 @@ public class SessionsController : ControllerBase private readonly ISessionRepository repository; private readonly IProjectAccessGuard accessGuard; + /// + /// Initializes a new instance of the class. + /// public SessionsController(ISessionRepository repository, IProjectAccessGuard accessGuard) { this.repository = repository; this.accessGuard = accessGuard; } + /// + /// Gets the all. + /// [HttpGet] public async Task> GetAll( [FromQuery] Guid projectId, @@ -40,6 +49,9 @@ public async Task> GetAll( return new PagedResult(items, total, page, pageSize).Map(SessionDto.From); } + /// + /// Gets. + /// [HttpGet("{id:guid}")] public async Task> Get(Guid id, CancellationToken cancellationToken) { diff --git a/Proxytrace.Api/Controllers/SetupController.cs b/Proxytrace.Api/Controllers/SetupController.cs index 9dba7a01a..4da63aa3c 100644 --- a/Proxytrace.Api/Controllers/SetupController.cs +++ b/Proxytrace.Api/Controllers/SetupController.cs @@ -15,6 +15,9 @@ namespace Proxytrace.Api.Controllers; +/// +/// API controller for setup operations. +/// [ApiController] [Authorize] [Route("api/setup")] @@ -28,6 +31,9 @@ public class SetupController : ControllerBase private readonly ILogger logger; private readonly IWebHostEnvironment env; + /// + /// Initializes a new instance of the class. + /// public SetupController( IRepository userRepository, IRepository projectRepository, @@ -46,6 +52,9 @@ public SetupController( this.env = env; } + /// + /// Gets the status. + /// [HttpGet("status")] [AllowAnonymous] public async Task GetStatus(CancellationToken cancellationToken) @@ -55,6 +64,9 @@ public async Task GetStatus(CancellationToken cancellationToken) return new SetupStatusDto { IsConfigured = users > 0 && projects > 0 }; } + /// + /// Completes. + /// [HttpPost("complete")] [Authorize(Roles = nameof(UserRole.Admin))] public async Task> Complete( @@ -87,6 +99,9 @@ public async Task> Complete( result.ProjectId); } + /// + /// Test connection. + /// [HttpPost("test-connection")] [Authorize(Roles = nameof(UserRole.Admin))] public async Task TestConnection( @@ -131,6 +146,9 @@ public async Task TestConnection( } } + /// + /// Lists the models. + /// [HttpPost("list-models")] [Authorize(Roles = nameof(UserRole.Admin))] public async Task ListModels( @@ -146,6 +164,9 @@ public async Task ListModels( return new ListModelsResponse(models); } + /// + /// Cleanup non model data. + /// [HttpPost("cleanup")] [Authorize(Roles = nameof(UserRole.Admin))] public async Task CleanupNonModelData(CancellationToken cancellationToken) diff --git a/Proxytrace.Api/Controllers/StatisticsController.cs b/Proxytrace.Api/Controllers/StatisticsController.cs index aa14043d2..805e4a5c4 100644 --- a/Proxytrace.Api/Controllers/StatisticsController.cs +++ b/Proxytrace.Api/Controllers/StatisticsController.cs @@ -13,6 +13,9 @@ namespace Proxytrace.Api.Controllers; +/// +/// API controller for statistics operations. +/// [ApiController] [Authorize] [Route("api/statistics")] @@ -27,6 +30,9 @@ public class StatisticsController : ControllerBase private readonly StatisticsOptions options; private readonly IProjectAccessGuard accessGuard; + /// + /// Initializes a new instance of the class. + /// public StatisticsController( IDashboardStatistics dashboard, IAgentStatistics agentStatistics, @@ -47,6 +53,9 @@ public StatisticsController( this.accessGuard = accessGuard; } + /// + /// Gets the dashboard view. + /// [HttpGet("dashboard")] public async Task> GetDashboardView( [FromQuery] DateTimeOffset? from = null, @@ -192,6 +201,9 @@ public async Task> GetCostOverview( }); } + /// + /// Gets the agent overview. + /// [HttpGet("agents/{agentId:guid}/overview")] public async Task> GetAgentOverview( Guid agentId, @@ -216,6 +228,9 @@ public async Task> GetAgentOverview( Counts: ToDto(result.Counts)); } + /// + /// Gets the agent distributions. + /// [HttpGet("agents/{agentId:guid}/distributions")] public async Task> GetAgentDistributions( Guid agentId, diff --git a/Proxytrace.Api/Controllers/TestCasesController.cs b/Proxytrace.Api/Controllers/TestCasesController.cs index 205dcca39..07efc27b1 100644 --- a/Proxytrace.Api/Controllers/TestCasesController.cs +++ b/Proxytrace.Api/Controllers/TestCasesController.cs @@ -11,6 +11,9 @@ namespace Proxytrace.Api.Controllers; +/// +/// API controller for test cases operations. +/// [ApiController] [Authorize] [Route("api/test-cases")] @@ -23,6 +26,9 @@ public class TestCasesController : ControllerBase private readonly IProjectAccessGuard accessGuard; private readonly ILogger audit; + /// + /// Initializes a new instance of the class. + /// public TestCasesController( IRepository repository, ITestSuiteRepository suiteRepository, @@ -56,6 +62,9 @@ private async Task CanAccessTestCaseAsync(Guid testCaseId, CancellationTok return false; } + /// + /// Gets. + /// [HttpGet("{id:guid}")] public async Task> Get(Guid id, CancellationToken cancellationToken) { @@ -65,6 +74,9 @@ public async Task> Get(Guid id, CancellationToken canc return ToDto(tc); } + /// + /// Updates. + /// [HttpPut("{id:guid}")] public async Task> Update( Guid id, diff --git a/Proxytrace.Api/Controllers/TestRunGroupsController.cs b/Proxytrace.Api/Controllers/TestRunGroupsController.cs index cb574d8a5..516edb09f 100644 --- a/Proxytrace.Api/Controllers/TestRunGroupsController.cs +++ b/Proxytrace.Api/Controllers/TestRunGroupsController.cs @@ -19,6 +19,9 @@ namespace Proxytrace.Api.Controllers; +/// +/// API controller for test run groups operations. +/// [ApiController] [Authorize] [Route("api/test-run-groups")] @@ -36,6 +39,9 @@ public class TestRunGroupsController : ControllerBase private readonly IProjectAccessGuard accessGuard; private readonly ILogger audit; + /// + /// Initializes a new instance of the class. + /// public TestRunGroupsController( ITestRunGroupRepository groupRepository, ITestRunRepository runRepository, @@ -93,6 +99,9 @@ public TestRunGroupsController( return scope; } + /// + /// Gets the all. + /// [HttpGet] public async Task> GetAll( [FromQuery] Guid? suiteId = null, @@ -122,6 +131,9 @@ public async Task> GetAll( return new PagedResult(items, paged.Total, paged.Page, paged.PageSize); } + /// + /// Gets. + /// [HttpGet("{id:guid}")] public async Task> Get(Guid id, CancellationToken cancellationToken) { @@ -133,6 +145,9 @@ public async Task> Get(Guid id, CancellationToken return await ToDtoAsync(group, cancellationToken); } + /// + /// Creates. + /// [HttpPost] public async Task> Create( [FromBody] CreateTestRunGroupRequest request, @@ -166,6 +181,9 @@ public async Task> Create( return AcceptedAtAction(nameof(Get), new { id = group.Id }, await ToDtoAsync(group, cancellationToken)); } + /// + /// Stream. + /// [HttpGet("{id:guid}/stream")] public async Task Stream(Guid id, CancellationToken cancellationToken) { @@ -213,6 +231,9 @@ public async Task Stream(Guid id, CancellationToken cancellationToken) } } + /// + /// Optimize. + /// [HttpPost("{id:guid}/optimize")] public async Task Optimize(Guid id, CancellationToken cancellationToken) { @@ -230,6 +251,9 @@ public async Task Optimize(Guid id, CancellationToken cancellatio return Accepted(); } + /// + /// Cancels. + /// [HttpPost("{id:guid}/cancel")] public async Task> Cancel(Guid id, CancellationToken cancellationToken) { @@ -245,6 +269,9 @@ public async Task> Cancel(Guid id, CancellationTok return AcceptedAtAction(nameof(Get), new { id = group.Id }, await ToDtoAsync(group, cancellationToken)); } + /// + /// Deletes. + /// [HttpDelete("{id:guid}")] public async Task Delete(Guid id, CancellationToken cancellationToken) { diff --git a/Proxytrace.Api/Controllers/TestRunSchedulesController.cs b/Proxytrace.Api/Controllers/TestRunSchedulesController.cs index 31a74d964..38b952e63 100644 --- a/Proxytrace.Api/Controllers/TestRunSchedulesController.cs +++ b/Proxytrace.Api/Controllers/TestRunSchedulesController.cs @@ -17,6 +17,9 @@ namespace Proxytrace.Api.Controllers; +/// +/// API controller for test run schedules operations. +/// [ApiController] [Authorize] [Route("api/test-run-schedules")] @@ -34,6 +37,9 @@ public class TestRunSchedulesController : ControllerBase private readonly IProjectAccessGuard accessGuard; private readonly ILogger audit; + /// + /// Initializes a new instance of the class. + /// public TestRunSchedulesController( ITestRunScheduleRepository scheduleRepository, ITestRunGroupRepository groupRepository, @@ -108,6 +114,9 @@ public async Task> GetAll( return await Task.WhenAll(schedules.Select(s => ToDtoAsync(s, cancellationToken))); } + /// + /// Gets. + /// [HttpGet("{id:guid}")] public async Task> Get(Guid id, CancellationToken cancellationToken) { @@ -119,6 +128,9 @@ public async Task> Get(Guid id, CancellationTok return await ToDtoAsync(schedule, cancellationToken); } + /// + /// Creates. + /// [HttpPost] [RequiresFeature(LicenseFeature.ScheduledTestRuns)] public async Task> Create( @@ -155,6 +167,9 @@ public async Task> Create( return CreatedAtAction(nameof(Get), new { id = schedule.Id }, await ToDtoAsync(schedule, cancellationToken)); } + /// + /// Updates. + /// [HttpPatch("{id:guid}")] [RequiresFeature(LicenseFeature.ScheduledTestRuns)] public async Task> Update( @@ -191,6 +206,9 @@ public async Task> Update( return await ToDtoAsync(schedule, cancellationToken); } + /// + /// Deletes. + /// [HttpDelete("{id:guid}")] [RequiresFeature(LicenseFeature.ScheduledTestRuns)] public async Task Delete(Guid id, CancellationToken cancellationToken) @@ -209,6 +227,9 @@ public async Task Delete(Guid id, CancellationToken cancellationT return NoContent(); } + /// + /// Runs the now. + /// [HttpPost("{id:guid}/run-now")] [RequiresFeature(LicenseFeature.ScheduledTestRuns)] public async Task> RunNow(Guid id, CancellationToken cancellationToken) diff --git a/Proxytrace.Api/Controllers/TestRunsController.cs b/Proxytrace.Api/Controllers/TestRunsController.cs index 9a48ee3e0..f8c3a5564 100644 --- a/Proxytrace.Api/Controllers/TestRunsController.cs +++ b/Proxytrace.Api/Controllers/TestRunsController.cs @@ -14,6 +14,9 @@ namespace Proxytrace.Api.Controllers; +/// +/// API controller for test runs operations. +/// [ApiController] [Authorize] [Route("api/test-runs")] @@ -26,6 +29,9 @@ public class TestRunsController : ControllerBase private readonly IProjectAccessGuard accessGuard; private readonly ILogger audit; + /// + /// Initializes a new instance of the class. + /// public TestRunsController( ITestRunRepository repository, IAgentRepository agentRepository, @@ -42,6 +48,9 @@ public TestRunsController( this.audit = audit; } + /// + /// Gets the all. + /// [HttpGet] public async Task> GetAll( [FromQuery] Guid? agentId = null, @@ -74,6 +83,9 @@ public async Task> GetAll( return paged.Map(mapper.ToDto); } + /// + /// Gets. + /// [HttpGet("{id:guid}")] public async Task> Get(Guid id, CancellationToken cancellationToken) { @@ -85,6 +97,9 @@ public async Task> Get(Guid id, CancellationToken cance return mapper.ToDto(run); } + /// + /// Gets the case fixture. + /// [HttpGet("{id:guid}/cases/{caseId:guid}/fixture")] public async Task> GetCaseFixture( Guid id, Guid caseId, CancellationToken cancellationToken) @@ -100,6 +115,9 @@ public async Task> GetCaseFixture( return mapper.ToFixtureDto(run, result); } + /// + /// Gets the case request. + /// [HttpGet("{id:guid}/cases/{caseId:guid}/request")] public async Task> GetCaseRequest( Guid id, Guid caseId, CancellationToken cancellationToken) @@ -118,6 +136,9 @@ public async Task> GetCaseRequest( return mapper.ToRequestDto(preview); } + /// + /// Stream. + /// [HttpGet("{id:guid}/stream")] public async Task Stream(Guid id, CancellationToken cancellationToken) { @@ -167,6 +188,9 @@ public async Task Stream(Guid id, CancellationToken cancellationToken) } } + /// + /// Deletes. + /// [HttpDelete("{id:guid}")] public async Task Delete(Guid id, CancellationToken cancellationToken) { diff --git a/Proxytrace.Api/Controllers/TestSuitesController.cs b/Proxytrace.Api/Controllers/TestSuitesController.cs index db4ecdeb9..a2fe791e7 100644 --- a/Proxytrace.Api/Controllers/TestSuitesController.cs +++ b/Proxytrace.Api/Controllers/TestSuitesController.cs @@ -18,6 +18,9 @@ namespace Proxytrace.Api.Controllers; +/// +/// API controller for test suites operations. +/// [ApiController] [Authorize] [Route("api/test-suites")] @@ -53,6 +56,9 @@ public class TestSuitesController : ControllerBase /// private const string SuiteQuotaLockKey = "license-quota:test-suites"; + /// + /// Initializes a new instance of the class. + /// public TestSuitesController( ITestSuiteRepository suiteRepository, IAgentRepository agentRepository, @@ -128,6 +134,9 @@ private async Task CanAccessTestCasesAsync( return true; } + /// + /// Gets the all. + /// [HttpGet] public async Task> GetAll( [FromQuery] Guid? agentId = null, @@ -187,6 +196,9 @@ private async Task>> GetRu } + /// + /// Gets. + /// [HttpGet("{id:guid}")] public async Task> Get(Guid id, CancellationToken cancellationToken) { @@ -224,6 +236,9 @@ public async Task> GetRunStats( return mapper.ToRunStatsDto(rows); } + /// + /// Creates. + /// [HttpPost] public async Task> Create( [FromBody] CreateTestSuiteRequest request, @@ -276,6 +291,9 @@ public async Task> Create( return CreatedAtAction(nameof(Get), new { id = savedSuite.Id }, mapper.ToDto(savedSuite)); } + /// + /// Updates. + /// [HttpPut("{id:guid}")] public async Task> Update( Guid id, @@ -331,6 +349,9 @@ public async Task> Update( // Deleting a suite cascades to its run groups, runs, schedules, theories, and the proposals // produced from those runs (see the storage FK config) — so the delete always succeeds. The // DbUpdateExceptionMapper middleware still maps any unforeseen constraint to a friendly 409. + /// + /// Deletes. + /// [HttpDelete("{id:guid}")] public async Task Delete(Guid id, CancellationToken cancellationToken) { @@ -417,6 +438,9 @@ public async Task> PromoteFromTraces( return CreatedAtAction(nameof(Get), new { id = savedSuite.Id }, mapper.ToDto(savedSuite)); } + /// + /// Adds the test case. + /// [HttpPost("{id:guid}/test-cases")] public async Task> AddTestCase( Guid id, @@ -442,6 +466,9 @@ public async Task> AddTestCase( return mapper.ToDto(savedSuite); } + /// + /// Removes the test case. + /// [HttpDelete("{id:guid}/test-cases/{caseId:guid}")] public async Task> RemoveTestCase( Guid id, diff --git a/Proxytrace.Api/Controllers/TestSupportController.cs b/Proxytrace.Api/Controllers/TestSupportController.cs index d6288f212..0b6b37a24 100644 --- a/Proxytrace.Api/Controllers/TestSupportController.cs +++ b/Proxytrace.Api/Controllers/TestSupportController.cs @@ -18,6 +18,9 @@ public class TestSupportController : ControllerBase private readonly ITestDataReset reset; private readonly ILogger logger; + /// + /// Initializes a new instance of the class. + /// public TestSupportController(ITestDataReset reset, ILogger logger) { this.reset = reset; diff --git a/Proxytrace.Api/Controllers/TheoriesController.cs b/Proxytrace.Api/Controllers/TheoriesController.cs index 3bf7191da..284fc4347 100644 --- a/Proxytrace.Api/Controllers/TheoriesController.cs +++ b/Proxytrace.Api/Controllers/TheoriesController.cs @@ -19,6 +19,9 @@ namespace Proxytrace.Api.Controllers; +/// +/// API controller for theories operations. +/// [ApiController] [Authorize] [RequiresFeature(LicenseFeature.OptimizationProposals)] @@ -38,6 +41,9 @@ public class TheoriesController : ControllerBase private readonly IProjectAccessGuard accessGuard; private readonly ILogger audit; + /// + /// Initializes a new instance of the class. + /// public TheoriesController( IOptimizationTheoryRepository repository, ITheoryValidationService validationService, @@ -99,6 +105,9 @@ private async Task> ListScopedAsync( return scope is null ? all : all.Where(t => scope.Contains(t.Agent.Project.Id)).ToArray(); } + /// + /// Gets the all. + /// [HttpGet] public async Task> GetAll( [FromQuery] Guid? agentId = null, @@ -114,6 +123,9 @@ public async Task> GetAll( return theories.Select(mapper.ToDto).ToList(); } + /// + /// Gets. + /// [HttpGet("{id:guid}")] public async Task> Get(Guid id, CancellationToken cancellationToken) { diff --git a/Proxytrace.Api/Controllers/TraceyChatController.cs b/Proxytrace.Api/Controllers/TraceyChatController.cs index 6bd385b5e..53126b2c5 100644 --- a/Proxytrace.Api/Controllers/TraceyChatController.cs +++ b/Proxytrace.Api/Controllers/TraceyChatController.cs @@ -57,6 +57,9 @@ public class TraceyChatController : ControllerBase private readonly ICurrentUserAccessor currentUser; private readonly ILogger logger; + /// + /// Initializes a new instance of the class. + /// public TraceyChatController( IHttpClientFactory httpClientFactory, IIngestionExecutor ingestion, @@ -73,6 +76,9 @@ public TraceyChatController( this.logger = logger; } + /// + /// Forwards. + /// [Route("api/tracey/{projectId:guid}/openai/v1/{**path}")] [HttpPost] public async Task Forward(Guid projectId, string path, CancellationToken cancellationToken) diff --git a/Proxytrace.Api/Controllers/TraceyController.cs b/Proxytrace.Api/Controllers/TraceyController.cs index 37af21c75..5629875e7 100644 --- a/Proxytrace.Api/Controllers/TraceyController.cs +++ b/Proxytrace.Api/Controllers/TraceyController.cs @@ -11,6 +11,9 @@ namespace Proxytrace.Api.Controllers; +/// +/// API controller for tracey operations. +/// [ApiController] [Authorize] [RequiresFeature(LicenseFeature.Tracey)] @@ -21,6 +24,9 @@ public class TraceyController : ControllerBase private readonly IRepository projects; private readonly ICurrentUserAccessor currentUser; + /// + /// Initializes a new instance of the class. + /// public TraceyController( ITraceySessionService sessionService, IRepository projects, diff --git a/Proxytrace.Api/Controllers/UpdatesController.cs b/Proxytrace.Api/Controllers/UpdatesController.cs index f06a8513e..dba4d9375 100644 --- a/Proxytrace.Api/Controllers/UpdatesController.cs +++ b/Proxytrace.Api/Controllers/UpdatesController.cs @@ -6,17 +6,26 @@ namespace Proxytrace.Api.Controllers; +/// +/// API controller for updates operations. +/// [ApiController] [Route("api/updates")] public class UpdatesController : ControllerBase { private readonly IUpdateService updateService; + /// + /// Initializes a new instance of the class. + /// public UpdatesController(IUpdateService updateService) { this.updateService = updateService; } + /// + /// Gets. + /// [HttpGet] [Authorize(Roles = nameof(UserRole.Admin))] public UpdateStatusDto Get() diff --git a/Proxytrace.Api/Controllers/UsersController.cs b/Proxytrace.Api/Controllers/UsersController.cs index 89bb6d22d..0c7cf854f 100644 --- a/Proxytrace.Api/Controllers/UsersController.cs +++ b/Proxytrace.Api/Controllers/UsersController.cs @@ -14,6 +14,9 @@ namespace Proxytrace.Api.Controllers; +/// +/// API controller for users operations. +/// [ApiController] [Authorize] [Route("api/users")] @@ -29,6 +32,9 @@ public class UsersController : ControllerBase private readonly IConfiguration config; private readonly ILogger audit; + /// + /// Initializes a new instance of the class. + /// public UsersController( IRepository repository, IProjectRepository projects, @@ -51,6 +57,9 @@ public UsersController( this.audit = audit; } + /// + /// Gets the all. + /// [HttpGet] [Authorize(Roles = nameof(UserRole.Admin))] public async Task> GetAll( @@ -63,6 +72,9 @@ public async Task> GetAll( return paged.Map(u => ToDto(u, mfaUsers.Contains(u.Id))); } + /// + /// Me. + /// [HttpGet("me")] public async Task> Me(CancellationToken cancellationToken) { @@ -108,6 +120,9 @@ public async Task UpdateMyEmailNotifications( return NoContent(); } + /// + /// Gets. + /// [HttpGet("{id:guid}")] [Authorize(Roles = nameof(UserRole.Admin))] public async Task> Get(Guid id, CancellationToken cancellationToken) @@ -118,6 +133,9 @@ public async Task> Get(Guid id, CancellationToken cancella return ToDto(user, await mfa.IsEnabledAsync(user.Id, cancellationToken)); } + /// + /// Gets the projects. + /// [HttpGet("{id:guid}/projects")] [Authorize(Roles = nameof(UserRole.Admin))] public async Task>> GetProjects( @@ -130,6 +148,9 @@ public async Task>> GetProjects( return memberships.Select(p => new UserProjectDto(p.Id, p.Name)).ToArray(); } + /// + /// Updates the role. + /// [HttpPut("{id:guid}/role")] [Authorize(Roles = nameof(UserRole.Admin))] public async Task> UpdateRole( @@ -147,6 +168,9 @@ public async Task> UpdateRole( return ToDto(updated, await mfa.IsEnabledAsync(updated.Id, cancellationToken)); } + /// + /// Deletes. + /// [HttpDelete("{id:guid}")] [Authorize(Roles = nameof(UserRole.Admin))] public async Task Delete(Guid id, CancellationToken cancellationToken) diff --git a/Proxytrace.Api/Debug/DebugLoginSeederHostedService.cs b/Proxytrace.Api/Debug/DebugLoginSeederHostedService.cs index b435ca6bb..0ebfe7f70 100644 --- a/Proxytrace.Api/Debug/DebugLoginSeederHostedService.cs +++ b/Proxytrace.Api/Debug/DebugLoginSeederHostedService.cs @@ -37,6 +37,9 @@ internal sealed class DebugLoginSeederHostedService : IHostedService private readonly IServiceProvider rootServices; private readonly ILogger logger; + /// + /// Initializes a new instance of the class. + /// public DebugLoginSeederHostedService( IServiceProvider rootServices, ILogger logger) @@ -45,6 +48,9 @@ public DebugLoginSeederHostedService( this.logger = logger; } + /// + /// Starts asynchronously. + /// public async Task StartAsync(CancellationToken cancellationToken) { using var scope = rootServices.CreateScope(); @@ -75,6 +81,9 @@ public async Task StartAsync(CancellationToken cancellationToken) Email); } + /// + /// Stops asynchronously. + /// public Task StopAsync(CancellationToken cancellationToken) => Task.CompletedTask; } #endif diff --git a/Proxytrace.Api/Dto/AgentCalls/AgentCallDto.cs b/Proxytrace.Api/Dto/AgentCalls/AgentCallDto.cs index 9c4dcd3df..d3133d562 100644 --- a/Proxytrace.Api/Dto/AgentCalls/AgentCallDto.cs +++ b/Proxytrace.Api/Dto/AgentCalls/AgentCallDto.cs @@ -3,6 +3,9 @@ namespace Proxytrace.Api.Dto.AgentCalls; +/// +/// Data transfer object representing a agent call. +/// public record AgentCallDto( Guid Id, Guid? AgentId, @@ -27,8 +30,14 @@ public record AgentCallDto( Guid? SessionId, int OutlierFlags); +/// +/// Data transfer object representing a agent call message. +/// public record AgentCallMessageDto(string Role, string Content, IReadOnlyList ToolRequests, string? ToolCallId = null); +/// +/// Data transfer object representing a agent call tool request. +/// public record AgentCallToolRequestDto(string Id, string Name, string Arguments); /// diff --git a/Proxytrace.Api/Dto/AgentCalls/AgentCallDtoMapper.cs b/Proxytrace.Api/Dto/AgentCalls/AgentCallDtoMapper.cs index 3ada571e9..0436cfdcf 100644 --- a/Proxytrace.Api/Dto/AgentCalls/AgentCallDtoMapper.cs +++ b/Proxytrace.Api/Dto/AgentCalls/AgentCallDtoMapper.cs @@ -14,11 +14,17 @@ public sealed class AgentCallDtoMapper { private readonly ToolDtoMapper toolDtoMapper; + /// + /// Initializes a new instance of the class. + /// public AgentCallDtoMapper(ToolDtoMapper toolDtoMapper) { this.toolDtoMapper = toolDtoMapper; } + /// + /// To dto. + /// public AgentCallDto ToDto(IAgentCall c) => new( c.Id, c.Agent.Id, diff --git a/Proxytrace.Api/Dto/AgentCalls/TraceHistogramBucketDto.cs b/Proxytrace.Api/Dto/AgentCalls/TraceHistogramBucketDto.cs index 2730581b3..c4bcbd952 100644 --- a/Proxytrace.Api/Dto/AgentCalls/TraceHistogramBucketDto.cs +++ b/Proxytrace.Api/Dto/AgentCalls/TraceHistogramBucketDto.cs @@ -1,3 +1,6 @@ namespace Proxytrace.Api.Dto.AgentCalls; +/// +/// Data transfer object representing a trace histogram bucket. +/// public record TraceHistogramBucketDto(DateTimeOffset Start, int Total, int Errors); diff --git a/Proxytrace.Api/Dto/Agents/AgentDto.cs b/Proxytrace.Api/Dto/Agents/AgentDto.cs index b0c304e5c..bef437459 100644 --- a/Proxytrace.Api/Dto/Agents/AgentDto.cs +++ b/Proxytrace.Api/Dto/Agents/AgentDto.cs @@ -2,6 +2,9 @@ namespace Proxytrace.Api.Dto.Agents; +/// +/// Data transfer object representing a agent. +/// public record AgentDto( Guid Id, Guid ProjectId, @@ -36,10 +39,19 @@ public record AgentListItemDto( DateTimeOffset UpdatedAt, DateTimeOffset? LastUsedAt); +/// +/// Request payload for update agent endpoint operations. +/// public record UpdateAgentEndpointRequest(Guid EndpointId); +/// +/// Request payload for move version operations. +/// public record MoveVersionRequest(Guid TargetAgentId); +/// +/// Data transfer object representing a agent version. +/// public record AgentVersionDto( Guid Id, Guid AgentId, @@ -49,11 +61,17 @@ public record AgentVersionDto( string Fingerprint, DateTimeOffset CreatedAt); +/// +/// Data transfer object representing a tool specification. +/// public record ToolSpecificationDto( string Name, string Description, IReadOnlyList Arguments); +/// +/// Data transfer object representing a tool argument. +/// public record ToolArgumentDto( string Name, string? Description, diff --git a/Proxytrace.Api/Dto/Agents/AgentDtoMapper.cs b/Proxytrace.Api/Dto/Agents/AgentDtoMapper.cs index dc4af8bb5..2b5c896c3 100644 --- a/Proxytrace.Api/Dto/Agents/AgentDtoMapper.cs +++ b/Proxytrace.Api/Dto/Agents/AgentDtoMapper.cs @@ -13,11 +13,17 @@ public sealed class AgentDtoMapper { private readonly ToolDtoMapper toolDtoMapper; + /// + /// Initializes a new instance of the class. + /// public AgentDtoMapper(ToolDtoMapper toolDtoMapper) { this.toolDtoMapper = toolDtoMapper; } + /// + /// To dto. + /// public AgentDto ToDto(IAgent a, DateTimeOffset? lastUsedAt) => new( a.Id, a.Project.Id, @@ -50,6 +56,9 @@ [.. a.Tools.Select(toolDtoMapper.ToToolSpecDto)], a.UpdatedAt, lastUsedAt); + /// + /// To dto. + /// public AgentVersionDto ToDto(IAgentVersion v, string fingerprint) => new( v.Id, v.AgentId, diff --git a/Proxytrace.Api/Dto/Anomalies/CustomAnomalyDetectorDtos.cs b/Proxytrace.Api/Dto/Anomalies/CustomAnomalyDetectorDtos.cs index f1a50b3fe..5f3c168e6 100644 --- a/Proxytrace.Api/Dto/Anomalies/CustomAnomalyDetectorDtos.cs +++ b/Proxytrace.Api/Dto/Anomalies/CustomAnomalyDetectorDtos.cs @@ -3,8 +3,14 @@ namespace Proxytrace.Api.Dto.Anomalies; +/// +/// Data transfer object representing a anomaly trigger. +/// public record AnomalyTriggerDto(TriggerKind Kind, string Pattern); +/// +/// Data transfer object representing a custom anomaly detector. +/// public record CustomAnomalyDetectorDto( Guid Id, string Name, @@ -20,9 +26,18 @@ public record CustomAnomalyDetectorDto( DateTimeOffset CreatedAt, DateTimeOffset UpdatedAt); +/// +/// Request payload for create custom anomaly detector operations. +/// public sealed record CreateCustomAnomalyDetectorRequest { + /// + /// Gets or sets the project id. + /// public required Guid ProjectId { get; init; } + /// + /// Gets or sets the name. + /// public required string Name { get; init; } /// The LLM review instructions — become the hidden system agent's system prompt. @@ -31,28 +46,61 @@ public sealed record CreateCustomAnomalyDetectorRequest /// The model endpoint the hidden system agent reviews with. public required Guid EndpointId { get; init; } + /// + /// Gets or sets the triggers. + /// public required IReadOnlyList Triggers { get; init; } + /// + /// Gets or sets the all agents. + /// public bool AllAgents { get; init; } = true; + /// + /// Gets or sets the agent ids. + /// [MaxLength(RequestLimits.MaxScopedAgents)] public IReadOnlyList? AgentIds { get; init; } + /// + /// Gets or sets the is enabled. + /// public bool IsEnabled { get; init; } = true; /// Whether the proxy rejects trigger-matching requests before they reach the provider. public bool BlockUpstream { get; init; } } +/// +/// Request payload for update custom anomaly detector operations. +/// public sealed record UpdateCustomAnomalyDetectorRequest { + /// + /// Gets or sets the name. + /// public required string Name { get; init; } + /// + /// Gets or sets the instructions. + /// public required string Instructions { get; init; } /// Null keeps the hidden agent's current endpoint. public Guid? EndpointId { get; init; } + /// + /// Gets or sets the triggers. + /// public required IReadOnlyList Triggers { get; init; } + /// + /// Gets or sets the all agents. + /// public required bool AllAgents { get; init; } + /// + /// Gets or sets the agent ids. + /// [MaxLength(RequestLimits.MaxScopedAgents)] public IReadOnlyList? AgentIds { get; init; } + /// + /// Gets or sets the is enabled. + /// public required bool IsEnabled { get; init; } /// Whether the proxy rejects trigger-matching requests before they reach the provider. diff --git a/Proxytrace.Api/Dto/ApiKeys/ApiKeyDto.cs b/Proxytrace.Api/Dto/ApiKeys/ApiKeyDto.cs index b7873556a..af9e3c1eb 100644 --- a/Proxytrace.Api/Dto/ApiKeys/ApiKeyDto.cs +++ b/Proxytrace.Api/Dto/ApiKeys/ApiKeyDto.cs @@ -2,6 +2,9 @@ namespace Proxytrace.Api.Dto.ApiKeys; +/// +/// Data transfer object representing a api key. +/// public record ApiKeyDto( Guid Id, string Name, @@ -18,6 +21,9 @@ public record ApiKeyDto( // shown again. Null in every list/overview response. string? PlaintextKey = null); +/// +/// Request payload for create api key operations. +/// public record CreateApiKeyRequest( string Name, Guid ProjectId, diff --git a/Proxytrace.Api/Dto/ApplicationErrors/ApplicationErrorDto.cs b/Proxytrace.Api/Dto/ApplicationErrors/ApplicationErrorDto.cs index 0e884545a..5bef64931 100644 --- a/Proxytrace.Api/Dto/ApplicationErrors/ApplicationErrorDto.cs +++ b/Proxytrace.Api/Dto/ApplicationErrors/ApplicationErrorDto.cs @@ -2,6 +2,9 @@ namespace Proxytrace.Api.Dto.ApplicationErrors; +/// +/// Data transfer object representing a application error. +/// public record ApplicationErrorDto( Guid Id, string Message, diff --git a/Proxytrace.Api/Dto/AuditLog/AuditLogEntryDto.cs b/Proxytrace.Api/Dto/AuditLog/AuditLogEntryDto.cs index e569644a6..ff22cefeb 100644 --- a/Proxytrace.Api/Dto/AuditLog/AuditLogEntryDto.cs +++ b/Proxytrace.Api/Dto/AuditLog/AuditLogEntryDto.cs @@ -2,6 +2,9 @@ namespace Proxytrace.Api.Dto.AuditLog; +/// +/// Data transfer object representing a audit log entry. +/// public record AuditLogEntryDto( Guid Id, AuditAction Action, diff --git a/Proxytrace.Api/Dto/Auth/AuthDtos.cs b/Proxytrace.Api/Dto/Auth/AuthDtos.cs index 55c179cdf..8b2f3367c 100644 --- a/Proxytrace.Api/Dto/Auth/AuthDtos.cs +++ b/Proxytrace.Api/Dto/Auth/AuthDtos.cs @@ -3,11 +3,29 @@ namespace Proxytrace.Api.Dto.Auth; +/// +/// Data transfer object representing a auth mode. +/// public record AuthModeDto(string Mode, bool SetupRequired, bool LegacyClaimAvailable); +/// +/// Request payload for login operations. +/// public record LoginRequest(string Email, string Password); +/// +/// Request payload for claim legacy operations. +/// public record ClaimLegacyRequest(string Email, string Password); +/// +/// Request payload for signup operations. +/// public record SignupRequest(string Token, string Password); +/// +/// Request payload for setup admin operations. +/// public record SetupAdminRequest(string Email, string Password); +/// +/// Response payload for token operations. +/// public record TokenResponse(string Token, DateTimeOffset ExpiresAt); /// @@ -21,11 +39,29 @@ public record LoginResponseDto( bool MfaRequired, string? MfaChallengeToken, DateTimeOffset? MfaChallengeExpiresAt); +/// +/// Request payload for mfa verify operations. +/// public record MfaVerifyRequest(string ChallengeToken, string Code); +/// +/// Request payload for mfa activate operations. +/// public record MfaActivateRequest(string Code); +/// +/// Request payload for mfa disable operations. +/// public record MfaDisableRequest(string Password); +/// +/// Response payload for mfa setup operations. +/// public record MfaSetupResponse(string Secret, string OtpAuthUri); +/// +/// Response payload for mfa activate operations. +/// public record MfaActivateResponse(IReadOnlyList BackupCodes); +/// +/// Data transfer object representing a me. +/// public record MeDto( Guid Id, string Email, @@ -35,10 +71,31 @@ public record MeDto( NotificationSeverity EmailNotificationMinSeverity, bool EmailEnabled, bool MfaEnabled); +/// +/// Response payload for stream ticket operations. +/// public record StreamTicketResponse(string Token, DateTimeOffset ExpiresAt); +/// +/// Request payload for forgot password operations. +/// public record ForgotPasswordRequest(string Email); +/// +/// Request payload for reset password operations. +/// public record ResetPasswordRequest(string Token, string Password); +/// +/// Request payload for create invite operations. +/// public record CreateInviteRequest(string Email, UserRole Role); +/// +/// Data transfer object representing a invite. +/// public record InviteDto(Guid Id, string Email, UserRole Role, DateTimeOffset ExpiresAt, DateTimeOffset? ConsumedAt); +/// +/// Response payload for create invite operations. +/// public record CreateInviteResponse(string Token, string Url, DateTimeOffset ExpiresAt); +/// +/// Data transfer object representing a invite preview. +/// public record InvitePreviewDto(string Email, UserRole Role, DateTimeOffset ExpiresAt); diff --git a/Proxytrace.Api/Dto/Costs/CostDtos.cs b/Proxytrace.Api/Dto/Costs/CostDtos.cs index faf50da9d..724c0774f 100644 --- a/Proxytrace.Api/Dto/Costs/CostDtos.cs +++ b/Proxytrace.Api/Dto/Costs/CostDtos.cs @@ -41,8 +41,14 @@ public record UpdateCostLimitRequest( decimal? HardLimitEur, bool Enabled); +/// +/// Data transfer object representing a agent cost point. +/// public record AgentCostPointDto(DateTimeOffset BucketStart, Guid AgentId, decimal CostEur); +/// +/// Data transfer object representing a agent cost total. +/// public record AgentCostTotalDto(Guid AgentId, string AgentName, decimal CostEur); /// diff --git a/Proxytrace.Api/Dto/EmailSettings/EmailSettingsDtoMapper.cs b/Proxytrace.Api/Dto/EmailSettings/EmailSettingsDtoMapper.cs index a4a9fad4b..4346d5c40 100644 --- a/Proxytrace.Api/Dto/EmailSettings/EmailSettingsDtoMapper.cs +++ b/Proxytrace.Api/Dto/EmailSettings/EmailSettingsDtoMapper.cs @@ -1,7 +1,13 @@ namespace Proxytrace.Api.Dto.EmailSettings; +/// +/// Maps email settings dto between representations. +/// public sealed class EmailSettingsDtoMapper { + /// + /// To dto. + /// public EmailSettingsDto ToDto(Domain.Notifications.EmailSettings s) => new( s.Enabled, s.SmtpHost, s.SmtpPort, s.Security, s.Username, PasswordSet: !string.IsNullOrEmpty(s.Password), diff --git a/Proxytrace.Api/Dto/Evaluators/EvaluatorDtoMapper.cs b/Proxytrace.Api/Dto/Evaluators/EvaluatorDtoMapper.cs index f212a6168..4724bf050 100644 --- a/Proxytrace.Api/Dto/Evaluators/EvaluatorDtoMapper.cs +++ b/Proxytrace.Api/Dto/Evaluators/EvaluatorDtoMapper.cs @@ -8,6 +8,9 @@ namespace Proxytrace.Api.Dto.Evaluators; /// public sealed class EvaluatorDtoMapper { + /// + /// To dto. + /// public EvaluatorDetailDto ToDto(IEvaluator evaluator) { string? systemMessage = null; @@ -50,6 +53,9 @@ public EvaluatorDetailDto ToDto(IEvaluator evaluator) evaluator.UpdatedAt); } + /// + /// To recent dto. + /// public RecentEvaluationItemDto ToRecentDto(ITestResult r, Guid evaluatorId, Guid? runId) { var evaluation = r.Evaluations.FirstOrDefault(e => e.Evaluator.Id == evaluatorId); diff --git a/Proxytrace.Api/Dto/Evaluators/EvaluatorDtos.cs b/Proxytrace.Api/Dto/Evaluators/EvaluatorDtos.cs index 2ef50eedc..b12316dfd 100644 --- a/Proxytrace.Api/Dto/Evaluators/EvaluatorDtos.cs +++ b/Proxytrace.Api/Dto/Evaluators/EvaluatorDtos.cs @@ -10,6 +10,9 @@ namespace Proxytrace.Api.Dto.Evaluators; /// public record EvaluatorListItemDto(Guid Id, EvaluatorKind Kind, string Name); +/// +/// Data transfer object representing a evaluator detail. +/// public record EvaluatorDetailDto( Guid Id, EvaluatorKind Kind, @@ -26,6 +29,9 @@ public record EvaluatorDetailDto( DateTimeOffset CreatedAt, DateTimeOffset UpdatedAt); +/// +/// Request payload for create evaluator operations. +/// [JsonPolymorphic(TypeDiscriminatorPropertyName = "kind")] [JsonDerivedType(typeof(CreateAgenticEvaluatorRequest), nameof(EvaluatorKind.Agentic))] [JsonDerivedType(typeof(CreateExactMatchEvaluatorRequest), nameof(EvaluatorKind.ExactMatch))] @@ -33,28 +39,61 @@ public record EvaluatorDetailDto( [JsonDerivedType(typeof(CreateJsonSchemaMatchEvaluatorRequest), nameof(EvaluatorKind.JsonSchemaMatch))] public abstract record CreateEvaluatorRequest { + /// + /// Gets or sets the project id. + /// public required Guid ProjectId { get; init; } } +/// +/// Request payload for create agentic evaluator operations. +/// public sealed record CreateAgenticEvaluatorRequest : CreateEvaluatorRequest { + /// + /// Gets or sets the name. + /// public required string Name { get; init; } + /// + /// Gets or sets the system message. + /// public required string SystemMessage { get; init; } } +/// +/// Request payload for create exact match evaluator operations. +/// public sealed record CreateExactMatchEvaluatorRequest : CreateEvaluatorRequest; +/// +/// Request payload for create numeric match evaluator operations. +/// public sealed record CreateNumericMatchEvaluatorRequest : CreateEvaluatorRequest { + /// + /// Gets or sets the extraction pattern. + /// public required string ExtractionPattern { get; init; } + /// + /// Gets or sets the tolerance. + /// public required decimal Tolerance { get; init; } } +/// +/// Request payload for create json schema match evaluator operations. +/// public sealed record CreateJsonSchemaMatchEvaluatorRequest : CreateEvaluatorRequest { + /// + /// Gets or sets the json schema. + /// public required string JsonSchema { get; init; } } +/// +/// Request payload for update evaluator operations. +/// [JsonPolymorphic(TypeDiscriminatorPropertyName = "kind")] [JsonDerivedType(typeof(UpdateAgenticEvaluatorRequest), nameof(EvaluatorKind.Agentic))] [JsonDerivedType(typeof(UpdateExactMatchEvaluatorRequest), nameof(EvaluatorKind.ExactMatch))] @@ -62,23 +101,53 @@ public sealed record CreateJsonSchemaMatchEvaluatorRequest : CreateEvaluatorRequ [JsonDerivedType(typeof(UpdateJsonSchemaMatchEvaluatorRequest), nameof(EvaluatorKind.JsonSchemaMatch))] public abstract record UpdateEvaluatorRequest; +/// +/// Request payload for update agentic evaluator operations. +/// public sealed record UpdateAgenticEvaluatorRequest : UpdateEvaluatorRequest { + /// + /// Gets or sets the name. + /// public string? Name { get; init; } + /// + /// Gets or sets the system message. + /// public string? SystemMessage { get; init; } } +/// +/// Request payload for update exact match evaluator operations. +/// public sealed record UpdateExactMatchEvaluatorRequest : UpdateEvaluatorRequest; +/// +/// Request payload for update numeric match evaluator operations. +/// public sealed record UpdateNumericMatchEvaluatorRequest : UpdateEvaluatorRequest { + /// + /// Gets or sets the extraction pattern. + /// public string? ExtractionPattern { get; init; } + /// + /// Gets or sets the tolerance. + /// public decimal? Tolerance { get; init; } } +/// +/// Request payload for update json schema match evaluator operations. +/// public sealed record UpdateJsonSchemaMatchEvaluatorRequest : UpdateEvaluatorRequest { + /// + /// Gets or sets the json schema. + /// public string? JsonSchema { get; init; } } +/// +/// Data transfer object representing a agentic evaluator preset. +/// public record AgenticEvaluatorPresetDto(string Key, string Name, string SystemPrompt); diff --git a/Proxytrace.Api/Dto/Evaluators/EvaluatorTestBenchDtos.cs b/Proxytrace.Api/Dto/Evaluators/EvaluatorTestBenchDtos.cs index e6cef05cd..a3782b1f4 100644 --- a/Proxytrace.Api/Dto/Evaluators/EvaluatorTestBenchDtos.cs +++ b/Proxytrace.Api/Dto/Evaluators/EvaluatorTestBenchDtos.cs @@ -3,6 +3,9 @@ namespace Proxytrace.Api.Dto.Evaluators; +/// +/// Data transfer object representing a evaluator test bench payload. +/// public record EvaluatorTestBenchPayloadDto( Guid SourceTestResultId, Guid TestCaseId, @@ -13,20 +16,32 @@ public record EvaluatorTestBenchPayloadDto( /// This evaluator's logged verdict on the source test result, when one exists — seeds the bench's baseline. EvaluationResultDto? LoggedEvaluation); +/// +/// Request payload for run evaluator on bench operations. +/// public record RunEvaluatorOnBenchRequest( Guid TestCaseId, string? ActualResponseOverride); +/// +/// Data transfer object representing a evaluator test bench default. +/// public record EvaluatorTestBenchDefaultDto( Guid? TestCaseId, string? Label); +/// +/// Data transfer object representing a evaluator test bench recent item. +/// public record EvaluatorTestBenchRecentItemDto( Guid TestCaseId, string Label, /// This evaluator's logged score on the recent result, when one exists. EvaluationScore? Score); +/// +/// Data transfer object representing a recent evaluation item. +/// public record RecentEvaluationItemDto( Guid TestResultId, Guid TestCaseId, diff --git a/Proxytrace.Api/Dto/Inference/ModelParametersDto.cs b/Proxytrace.Api/Dto/Inference/ModelParametersDto.cs index f1721bd03..a9241b68b 100644 --- a/Proxytrace.Api/Dto/Inference/ModelParametersDto.cs +++ b/Proxytrace.Api/Dto/Inference/ModelParametersDto.cs @@ -2,6 +2,9 @@ namespace Proxytrace.Api.Dto.Inference; +/// +/// Data transfer object representing a model parameters. +/// public record ModelParametersDto( double? Temperature, double? TopP, @@ -13,6 +16,9 @@ public record ModelParametersDto( IReadOnlyList? Stop, int? N) { + /// + /// From domain. + /// public static ModelParametersDto FromDomain(IModelParameters p) => new( p.Temperature, p.TopP, diff --git a/Proxytrace.Api/Dto/ModelProviders/ModelEndpointDto.cs b/Proxytrace.Api/Dto/ModelProviders/ModelEndpointDto.cs index 20f09be8d..537bbbade 100644 --- a/Proxytrace.Api/Dto/ModelProviders/ModelEndpointDto.cs +++ b/Proxytrace.Api/Dto/ModelProviders/ModelEndpointDto.cs @@ -1,5 +1,8 @@ namespace Proxytrace.Api.Dto.ModelProviders; +/// +/// Data transfer object representing a model endpoint. +/// public record ModelEndpointDto( Guid Id, string ModelName, @@ -13,10 +16,16 @@ public record ModelEndpointDto( DateTimeOffset CreatedAt, DateTimeOffset UpdatedAt); +/// +/// Request payload for create model endpoint operations. +/// public record CreateModelEndpointRequest( string ModelName, decimal? InputTokenCost, decimal? OutputTokenCost); +/// +/// Request payload for update model endpoint pricing operations. +/// public record UpdateModelEndpointPricingRequest( decimal? InputTokenCost, decimal? OutputTokenCost); diff --git a/Proxytrace.Api/Dto/ModelProviders/ModelProviderDto.cs b/Proxytrace.Api/Dto/ModelProviders/ModelProviderDto.cs index 04c877b7e..c150a1d6f 100644 --- a/Proxytrace.Api/Dto/ModelProviders/ModelProviderDto.cs +++ b/Proxytrace.Api/Dto/ModelProviders/ModelProviderDto.cs @@ -23,6 +23,9 @@ public record ModelProviderDto( /// The cleartext upstream credential, returned only from the audited reveal endpoint. public record ModelProviderKeyDto(string UpstreamApiKey); +/// +/// Request payload for create model provider operations. +/// public record CreateModelProviderRequest(string Name, string Endpoint, string UpstreamApiKey, ModelProviderKind Kind); /// diff --git a/Proxytrace.Api/Dto/ModelProviders/ModelProviderDtoMapper.cs b/Proxytrace.Api/Dto/ModelProviders/ModelProviderDtoMapper.cs index ea1eaa5c2..7eb057c3a 100644 --- a/Proxytrace.Api/Dto/ModelProviders/ModelProviderDtoMapper.cs +++ b/Proxytrace.Api/Dto/ModelProviders/ModelProviderDtoMapper.cs @@ -45,6 +45,9 @@ private static string Mask(string key) _ => key[..3] + new string('•', 8) + key[^4..], }; + /// + /// To key dto. + /// public ApiKeyDto ToKeyDto(IApiKey k) => ToKeyDto(k, plaintextKey: null); @@ -66,6 +69,9 @@ private static ApiKeyDto ToKeyDto(IApiKey k, string? plaintextKey) return new(k.Id, k.Name, k.KeyPrefix, k.Project.Id, k.Project.Name, k.Provider.Id, k.Provider.Name, scopes, k.Owner.Id, k.Owner.Email, k.CreatedAt, plaintextKey); } + /// + /// To endpoint dto. + /// public ModelEndpointDto ToEndpointDto(IModelEndpoint e) => new(e.Id, e.Model.Name, e.Provider.Id, e.Provider.Name, e.InputTokenCost, e.OutputTokenCost, e.CachedInputTokenCost, e.CreatedAt, e.UpdatedAt); } diff --git a/Proxytrace.Api/Dto/Notifications/NotificationDtoMapper.cs b/Proxytrace.Api/Dto/Notifications/NotificationDtoMapper.cs index e21c26643..8bff0e555 100644 --- a/Proxytrace.Api/Dto/Notifications/NotificationDtoMapper.cs +++ b/Proxytrace.Api/Dto/Notifications/NotificationDtoMapper.cs @@ -7,6 +7,9 @@ namespace Proxytrace.Api.Dto.Notifications; /// public sealed class NotificationDtoMapper { + /// + /// To dto. + /// public NotificationDto ToDto(INotification n) => new( n.Id, diff --git a/Proxytrace.Api/Dto/OutlierSettings/OutlierSettingsDtoMapper.cs b/Proxytrace.Api/Dto/OutlierSettings/OutlierSettingsDtoMapper.cs index 3eadeead3..99a0a5955 100644 --- a/Proxytrace.Api/Dto/OutlierSettings/OutlierSettingsDtoMapper.cs +++ b/Proxytrace.Api/Dto/OutlierSettings/OutlierSettingsDtoMapper.cs @@ -1,7 +1,13 @@ namespace Proxytrace.Api.Dto.OutlierSettings; +/// +/// Maps outlier settings dto between representations. +/// public sealed class OutlierSettingsDtoMapper { + /// + /// To dto. + /// public OutlierSettingsDto ToDto(Domain.Outliers.OutlierSettings s) => new( s.Enabled, s.SigmaMultiplier, s.MinSampleCount, s.SampleWindow); } diff --git a/Proxytrace.Api/Dto/Playground/PlaygroundDtos.cs b/Proxytrace.Api/Dto/Playground/PlaygroundDtos.cs index 61f3bcdb7..7d0c81ec4 100644 --- a/Proxytrace.Api/Dto/Playground/PlaygroundDtos.cs +++ b/Proxytrace.Api/Dto/Playground/PlaygroundDtos.cs @@ -1,5 +1,8 @@ namespace Proxytrace.Api.Dto.Playground; +/// +/// Data transfer object representing a playground complete request. +/// public sealed record PlaygroundCompleteRequestDto( Guid AgentId, Guid EndpointId, @@ -8,6 +11,9 @@ public sealed record PlaygroundCompleteRequestDto( IReadOnlyList Tools, IReadOnlyList Messages); +/// +/// Data transfer object representing a playground model parameters. +/// public sealed record PlaygroundModelParametersDto( double? Temperature, double? TopP, @@ -18,17 +24,26 @@ public sealed record PlaygroundModelParametersDto( long? Seed, IReadOnlyList? Stop); +/// +/// Data transfer object representing a playground tool specification. +/// public sealed record PlaygroundToolSpecificationDto( string Name, string Description, IReadOnlyList Arguments); +/// +/// Data transfer object representing a playground tool argument. +/// public sealed record PlaygroundToolArgumentDto( string Name, string? Description, string Type, bool IsRequired); +/// +/// Data transfer object representing a playground message. +/// public sealed record PlaygroundMessageDto( string Role, string Content, @@ -37,4 +52,7 @@ public sealed record PlaygroundMessageDto( bool ToolSucceeded, string? ToolError); +/// +/// Data transfer object representing a playground tool request. +/// public sealed record PlaygroundToolRequestDto(string Id, string Name, string Arguments); diff --git a/Proxytrace.Api/Dto/Projects/ProjectDto.cs b/Proxytrace.Api/Dto/Projects/ProjectDto.cs index e6401f131..786758bc3 100644 --- a/Proxytrace.Api/Dto/Projects/ProjectDto.cs +++ b/Proxytrace.Api/Dto/Projects/ProjectDto.cs @@ -2,6 +2,9 @@ namespace Proxytrace.Api.Dto.Projects; +/// +/// Data transfer object representing a project. +/// public record ProjectDto( Guid Id, string Name, @@ -23,8 +26,14 @@ public record ProjectListItemDto( DateTimeOffset CreatedAt, DateTimeOffset UpdatedAt); +/// +/// Data transfer object representing a project member. +/// public record ProjectMemberDto(Guid Id, string Email); +/// +/// Request payload for create project operations. +/// public record CreateProjectRequest( [Required, StringLength(200, MinimumLength = 1)] string Name, [Required] Guid SystemEndpointId, @@ -32,6 +41,9 @@ public record CreateProjectRequest( // Membership is an access-control primitive and must NOT be mass-assignable through this generic // "update name/endpoint" call — it changes only via the dedicated add/remove-member endpoints. +/// +/// Request payload for update project operations. +/// public record UpdateProjectRequest( [Required, StringLength(200, MinimumLength = 1)] string Name, [Required] Guid SystemEndpointId); diff --git a/Proxytrace.Api/Dto/Projects/ProjectDtoMapper.cs b/Proxytrace.Api/Dto/Projects/ProjectDtoMapper.cs index 2e0ad52e9..24e554847 100644 --- a/Proxytrace.Api/Dto/Projects/ProjectDtoMapper.cs +++ b/Proxytrace.Api/Dto/Projects/ProjectDtoMapper.cs @@ -9,6 +9,9 @@ namespace Proxytrace.Api.Dto.Projects; /// internal static class ProjectDtoMapper { + /// + /// To dto. + /// public static ProjectDto ToDto(IProject p) => new(p.Id, p.Name, @@ -26,6 +29,9 @@ public static ProjectListItemDto ToListItemDto(IProject p) => p.CreatedAt, p.UpdatedAt); + /// + /// To member dto. + /// public static ProjectMemberDto ToMemberDto(IUser user) => new(user.Id, user.Email); } diff --git a/Proxytrace.Api/Dto/Proposals/AbTestRunSummaryDto.cs b/Proxytrace.Api/Dto/Proposals/AbTestRunSummaryDto.cs index 619c301ac..5aa10a473 100644 --- a/Proxytrace.Api/Dto/Proposals/AbTestRunSummaryDto.cs +++ b/Proxytrace.Api/Dto/Proposals/AbTestRunSummaryDto.cs @@ -2,6 +2,9 @@ namespace Proxytrace.Api.Dto.Proposals; +/// +/// Data transfer object representing a ab test run summary. +/// public record AbTestRunSummaryDto( Guid Id, Guid GroupId, diff --git a/Proxytrace.Api/Dto/Proposals/OptimizationProposalDto.cs b/Proxytrace.Api/Dto/Proposals/OptimizationProposalDto.cs index f9f3febb7..29580ae7f 100644 --- a/Proxytrace.Api/Dto/Proposals/OptimizationProposalDto.cs +++ b/Proxytrace.Api/Dto/Proposals/OptimizationProposalDto.cs @@ -3,6 +3,9 @@ namespace Proxytrace.Api.Dto.Proposals; +/// +/// Data transfer object representing a optimization proposal. +/// public record OptimizationProposalDto( Guid Id, ProposalKind Kind, @@ -25,4 +28,7 @@ public record OptimizationProposalDto( DateTimeOffset UpdatedAt ); +/// +/// Request payload for update proposal status operations. +/// public record UpdateProposalStatusRequest(ProposalStatus Status); diff --git a/Proxytrace.Api/Dto/Proposals/OptimizationProposalDtoMapper.cs b/Proxytrace.Api/Dto/Proposals/OptimizationProposalDtoMapper.cs index 15f70e54c..94ff6e6d9 100644 --- a/Proxytrace.Api/Dto/Proposals/OptimizationProposalDtoMapper.cs +++ b/Proxytrace.Api/Dto/Proposals/OptimizationProposalDtoMapper.cs @@ -12,11 +12,17 @@ public sealed class OptimizationProposalDtoMapper { private readonly ToolDtoMapper toolDtoMapper; + /// + /// Initializes a new instance of the class. + /// public OptimizationProposalDtoMapper(ToolDtoMapper toolDtoMapper) { this.toolDtoMapper = toolDtoMapper; } + /// + /// To dto. + /// public OptimizationProposalDto ToDto(IOptimizationProposal p) => new( p.Id, @@ -41,6 +47,9 @@ public OptimizationProposalDto ToDto(IOptimizationProposal p) private const int ArtifactSchemaVersion = 1; + /// + /// To artifact dto. + /// public ProposalArtifactDto ToArtifactDto(IOptimizationProposal p) => new( ArtifactSchemaVersion, diff --git a/Proxytrace.Api/Dto/Proposals/ProposalArtifactDto.cs b/Proxytrace.Api/Dto/Proposals/ProposalArtifactDto.cs index f897ce871..e1946170f 100644 --- a/Proxytrace.Api/Dto/Proposals/ProposalArtifactDto.cs +++ b/Proxytrace.Api/Dto/Proposals/ProposalArtifactDto.cs @@ -22,8 +22,14 @@ public record ProposalArtifactDto( ProposalArtifactEvidenceDto Evidence, ProposalArtifactAdoptionDto Adoption); +/// +/// Data transfer object representing a proposal artifact agent. +/// public record ProposalArtifactAgentDto(Guid Id, string Name); +/// +/// Data transfer object representing a proposal artifact evidence. +/// public record ProposalArtifactEvidenceDto( double? CurrentPassRate, double? ProposedPassRate, @@ -31,6 +37,9 @@ public record ProposalArtifactEvidenceDto( Guid[] EvidenceTestRunIds, AbTestRunSummaryDto? AbTestRun); +/// +/// Data transfer object representing a proposal artifact adoption. +/// public record ProposalArtifactAdoptionDto( DateTimeOffset? AdoptedAt, Guid? AdoptedAgentVersionId, diff --git a/Proxytrace.Api/Dto/Proposals/ProposalDetailsDto.cs b/Proxytrace.Api/Dto/Proposals/ProposalDetailsDto.cs index d331cea07..225992323 100644 --- a/Proxytrace.Api/Dto/Proposals/ProposalDetailsDto.cs +++ b/Proxytrace.Api/Dto/Proposals/ProposalDetailsDto.cs @@ -3,6 +3,9 @@ namespace Proxytrace.Api.Dto.Proposals; +/// +/// Data transfer object representing a proposal details. +/// [JsonPolymorphic(TypeDiscriminatorPropertyName = "kind")] [JsonDerivedType(typeof(ModelSwitchDetailsDto), "ModelSwitch")] [JsonDerivedType(typeof(SystemPromptDetailsDto), "SystemPrompt")] @@ -11,6 +14,9 @@ namespace Proxytrace.Api.Dto.Proposals; [JsonDerivedType(typeof(ToolUpdateSeedDetailsDto), "ToolUpdateSeed")] public abstract record ProposalDetailsDto; +/// +/// Data transfer object representing a model switch details. +/// public record ModelSwitchDetailsDto( Guid EndpointId, string CurrentModelName, @@ -19,11 +25,17 @@ public record ModelSwitchDetailsDto( long? ExpectedLatencyMs ) : ProposalDetailsDto; +/// +/// Data transfer object representing a system prompt details. +/// public record SystemPromptDetailsDto( string CurrentSystemMessage, string ProposedSystemMessage ) : ProposalDetailsDto; +/// +/// Data transfer object representing a tool details. +/// public record ToolDetailsDto( IReadOnlyList CurrentTools, IReadOnlyList ProposedTools diff --git a/Proxytrace.Api/Dto/Search/SearchHitDto.cs b/Proxytrace.Api/Dto/Search/SearchHitDto.cs index 6ccfbae7d..ba9047908 100644 --- a/Proxytrace.Api/Dto/Search/SearchHitDto.cs +++ b/Proxytrace.Api/Dto/Search/SearchHitDto.cs @@ -1,5 +1,8 @@ namespace Proxytrace.Api.Dto.Search; +/// +/// Data transfer object representing a search hit. +/// public sealed record SearchHitDto( string Kind, Guid EntityId, diff --git a/Proxytrace.Api/Dto/Search/SearchIndexStatusDto.cs b/Proxytrace.Api/Dto/Search/SearchIndexStatusDto.cs index 5eda16bc7..f4206f8ae 100644 --- a/Proxytrace.Api/Dto/Search/SearchIndexStatusDto.cs +++ b/Proxytrace.Api/Dto/Search/SearchIndexStatusDto.cs @@ -1,5 +1,8 @@ namespace Proxytrace.Api.Dto.Search; +/// +/// Data transfer object representing a search index status. +/// public sealed record SearchIndexStatusDto( DateTimeOffset? LastIndexedAt, int DocumentCount, diff --git a/Proxytrace.Api/Dto/Search/SearchIndexingSettingsDto.cs b/Proxytrace.Api/Dto/Search/SearchIndexingSettingsDto.cs index d512e3583..da35f87ba 100644 --- a/Proxytrace.Api/Dto/Search/SearchIndexingSettingsDto.cs +++ b/Proxytrace.Api/Dto/Search/SearchIndexingSettingsDto.cs @@ -1,5 +1,8 @@ namespace Proxytrace.Api.Dto.Search; +/// +/// Data transfer object representing a search indexing settings. +/// public sealed record SearchIndexingSettingsDto( bool Enabled, IReadOnlyList IndexedKinds, diff --git a/Proxytrace.Api/Dto/Search/SearchResultsDto.cs b/Proxytrace.Api/Dto/Search/SearchResultsDto.cs index af02654e2..3e3b1a21c 100644 --- a/Proxytrace.Api/Dto/Search/SearchResultsDto.cs +++ b/Proxytrace.Api/Dto/Search/SearchResultsDto.cs @@ -1,3 +1,6 @@ namespace Proxytrace.Api.Dto.Search; +/// +/// Data transfer object representing a search results. +/// public sealed record SearchResultsDto(IReadOnlyList Hits); diff --git a/Proxytrace.Api/Dto/Sessions/SessionDto.cs b/Proxytrace.Api/Dto/Sessions/SessionDto.cs index aa38ba646..aa697648b 100644 --- a/Proxytrace.Api/Dto/Sessions/SessionDto.cs +++ b/Proxytrace.Api/Dto/Sessions/SessionDto.cs @@ -2,6 +2,9 @@ namespace Proxytrace.Api.Dto.Sessions; +/// +/// Data transfer object representing a session. +/// public record SessionDto( Guid Id, Guid ProjectId, @@ -11,6 +14,9 @@ public record SessionDto( int TraceCount, long TotalTokens) { + /// + /// From. + /// public static SessionDto From(ISession session) => new( session.Id, diff --git a/Proxytrace.Api/Dto/Setup/CompleteSetupRequest.cs b/Proxytrace.Api/Dto/Setup/CompleteSetupRequest.cs index fdb5f0541..c97149c17 100644 --- a/Proxytrace.Api/Dto/Setup/CompleteSetupRequest.cs +++ b/Proxytrace.Api/Dto/Setup/CompleteSetupRequest.cs @@ -2,6 +2,9 @@ namespace Proxytrace.Api.Dto.Setup; +/// +/// Request payload for complete setup operations. +/// public record CompleteSetupRequest( string ProviderName, string ProviderEndpoint, diff --git a/Proxytrace.Api/Dto/Setup/CompleteSetupResponse.cs b/Proxytrace.Api/Dto/Setup/CompleteSetupResponse.cs index f13a33147..e85b380ff 100644 --- a/Proxytrace.Api/Dto/Setup/CompleteSetupResponse.cs +++ b/Proxytrace.Api/Dto/Setup/CompleteSetupResponse.cs @@ -1,5 +1,8 @@ namespace Proxytrace.Api.Dto.Setup; +/// +/// Response payload for complete setup operations. +/// public record CompleteSetupResponse( Guid ProviderId, Guid EndpointId, diff --git a/Proxytrace.Api/Dto/Setup/ListModelsRequest.cs b/Proxytrace.Api/Dto/Setup/ListModelsRequest.cs index 89c4f69de..68596a6d3 100644 --- a/Proxytrace.Api/Dto/Setup/ListModelsRequest.cs +++ b/Proxytrace.Api/Dto/Setup/ListModelsRequest.cs @@ -2,6 +2,9 @@ namespace Proxytrace.Api.Dto.Setup; +/// +/// Request payload for list models operations. +/// public record ListModelsRequest( string ProviderName, string ProviderEndpoint, diff --git a/Proxytrace.Api/Dto/Setup/ListModelsResponse.cs b/Proxytrace.Api/Dto/Setup/ListModelsResponse.cs index a54eae153..e161732ad 100644 --- a/Proxytrace.Api/Dto/Setup/ListModelsResponse.cs +++ b/Proxytrace.Api/Dto/Setup/ListModelsResponse.cs @@ -1,3 +1,6 @@ namespace Proxytrace.Api.Dto.Setup; +/// +/// Response payload for list models operations. +/// public record ListModelsResponse(IReadOnlyList Models); diff --git a/Proxytrace.Api/Dto/Setup/SetupStatusDto.cs b/Proxytrace.Api/Dto/Setup/SetupStatusDto.cs index 85936c45d..2409e9d20 100644 --- a/Proxytrace.Api/Dto/Setup/SetupStatusDto.cs +++ b/Proxytrace.Api/Dto/Setup/SetupStatusDto.cs @@ -1,6 +1,12 @@ namespace Proxytrace.Api.Dto.Setup; +/// +/// Data transfer object representing a setup status. +/// public record SetupStatusDto { + /// + /// Gets or sets the is configured. + /// public required bool IsConfigured { get; init; } } diff --git a/Proxytrace.Api/Dto/Setup/TestConnectionRequest.cs b/Proxytrace.Api/Dto/Setup/TestConnectionRequest.cs index 3cc16c12c..7604e92c6 100644 --- a/Proxytrace.Api/Dto/Setup/TestConnectionRequest.cs +++ b/Proxytrace.Api/Dto/Setup/TestConnectionRequest.cs @@ -2,6 +2,9 @@ namespace Proxytrace.Api.Dto.Setup; +/// +/// Request payload for test connection operations. +/// public record TestConnectionRequest( string ProviderName, string ProviderEndpoint, diff --git a/Proxytrace.Api/Dto/Setup/TestConnectionResponse.cs b/Proxytrace.Api/Dto/Setup/TestConnectionResponse.cs index b69629f11..8ad052be4 100644 --- a/Proxytrace.Api/Dto/Setup/TestConnectionResponse.cs +++ b/Proxytrace.Api/Dto/Setup/TestConnectionResponse.cs @@ -1,5 +1,8 @@ namespace Proxytrace.Api.Dto.Setup; +/// +/// Response payload for test connection operations. +/// public record TestConnectionResponse( bool Success, string? ErrorCode, diff --git a/Proxytrace.Api/Dto/Statistics/EvaluatorStatsDtoMapper.cs b/Proxytrace.Api/Dto/Statistics/EvaluatorStatsDtoMapper.cs index 9685ca530..3daebe13a 100644 --- a/Proxytrace.Api/Dto/Statistics/EvaluatorStatsDtoMapper.cs +++ b/Proxytrace.Api/Dto/Statistics/EvaluatorStatsDtoMapper.cs @@ -8,12 +8,18 @@ namespace Proxytrace.Api.Dto.Statistics; /// internal static class EvaluatorStatsDtoMapper { + /// + /// To dto. + /// public static EvaluatorOverviewDto ToDto(EvaluatorOverviewStat result) => new( Summary: ToDto(result.Summary), PassRateTrend: result.PassRateTrend.Select(ToDto).ToArray(), ScoreDistribution: result.ScoreDistribution.Select(ToDto).ToArray(), CostTrend: result.CostTrend.Select(ToDto).ToArray()); + /// + /// To dto. + /// public static EvaluatorSparklineDto ToDto(EvaluatorSparklineStat s) => new(s.EvaluatorId, s.Points.Select(ToDto).ToArray()); diff --git a/Proxytrace.Api/Dto/Statistics/StatisticsDto.cs b/Proxytrace.Api/Dto/Statistics/StatisticsDto.cs index c2db7c2c7..fc55b544f 100644 --- a/Proxytrace.Api/Dto/Statistics/StatisticsDto.cs +++ b/Proxytrace.Api/Dto/Statistics/StatisticsDto.cs @@ -23,6 +23,9 @@ public record DashboardViewDto( /// Per-minute call counts over the trailing hour (60 entries, oldest → newest). IReadOnlyList Pulse); +/// +/// Data transfer object representing a summary. +/// public record SummaryDto( long TotalCalls, long TotalInputTokens, @@ -31,14 +34,29 @@ public record SummaryDto( double AvgLatencyMs, double? OverallPassRate); +/// +/// Data transfer object representing a token usage. +/// public record TokenUsageDto(DateTimeOffset BucketStart, Guid EndPointId, long InputTokens, long OutputTokens, long CachedInputTokens); +/// +/// Data transfer object representing a latency. +/// public record LatencyDto(Guid EndpointId, double P50Ms, double P95Ms, double P99Ms, double MinMs, double MaxMs, int SampleCount); +/// +/// Data transfer object representing a model breakdown. +/// public record ModelBreakdownDto(Guid EndpointId, string ModelName, int CallCount, long TotalInputTokens, long TotalOutputTokens, long TotalCachedInputTokens, double AvgDurationMs); +/// +/// Data transfer object representing a agent breakdown. +/// public record AgentBreakdownDto(Guid AgentId, int CallCount); +/// +/// Data transfer object representing a live telemetry. +/// public record LiveTelemetryDto( double TracesPerMinute, double TokensPerSecond, @@ -46,6 +64,9 @@ public record LiveTelemetryDto( double ErrorRate, double P95Ms); +/// +/// Data transfer object representing a agent token usage. +/// public record AgentTokenUsageDto(DateTimeOffset BucketStart, Guid AgentId, long InputTokens, long OutputTokens, long CachedInputTokens); /// @@ -54,12 +75,18 @@ public record AgentTokenUsageDto(DateTimeOffset BucketStart, Guid AgentId, long /// public record AgentAnomalyStatDto(DateTimeOffset BucketStart, Guid AgentId, int StaticCount, int CustomCount); +/// +/// Data transfer object representing a dashboard trends. +/// public record DashboardTrendsDto( IReadOnlyList Traces, IReadOnlyList LatencyMs, IReadOnlyList Throughput, IReadOnlyList PassRate); +/// +/// Data transfer object representing a agent time series point. +/// public record AgentTimeSeriesPointDto( DateTimeOffset BucketStart, int TraceCount, @@ -69,11 +96,17 @@ public record AgentTimeSeriesPointDto( decimal CostEur, double AvgLatencyMs); +/// +/// Data transfer object representing a agent pass rate point. +/// public record AgentPassRatePointDto( DateTimeOffset BucketStart, int Passed, int TestCases); +/// +/// Data transfer object representing a agent suite pass rate. +/// public record AgentSuitePassRateDto( Guid SuiteId, string SuiteName, @@ -81,12 +114,18 @@ public record AgentSuitePassRateDto( int Passed, int TestCases); +/// +/// Data transfer object representing a agent entity counts. +/// public record AgentEntityCountsDto( int SuiteCount, int TestCaseCount, int OpenProposalCount, int TotalProposalCount); +/// +/// Data transfer object representing a agent time summary. +/// public record AgentTimeSummaryDto( int TotalTraces, long TotalInputTokens, @@ -95,6 +134,9 @@ public record AgentTimeSummaryDto( decimal TotalCostEur, double AvgLatencyMs); +/// +/// Data transfer object representing a agent overview. +/// public record AgentOverviewDto( AgentTimeSummaryDto Summary, IReadOnlyList TimeSeries, @@ -102,11 +144,17 @@ public record AgentOverviewDto( IReadOnlyList SuitePassRates, AgentEntityCountsDto Counts); +/// +/// Data transfer object representing a histogram bin. +/// public record HistogramBinDto( double Start, double End, int Count); +/// +/// Data transfer object representing a metric distribution. +/// public record MetricDistributionDto( double Mean, double StdDev, @@ -115,6 +163,9 @@ public record MetricDistributionDto( double Max, IReadOnlyList Histogram); +/// +/// Data transfer object representing a agent distributions. +/// public record AgentDistributionsDto( MetricDistributionDto InputTokensPerCall, MetricDistributionDto OutputTokensPerCall, @@ -123,6 +174,9 @@ public record AgentDistributionsDto( MetricDistributionDto CacheHitRatePerConversation, MetricDistributionDto ToolCallsPerConversation); +/// +/// Data transfer object representing a evaluator summary. +/// public record EvaluatorSummaryDto( int TotalEvaluations, double? AvgScore, @@ -133,15 +187,24 @@ public record EvaluatorSummaryDto( decimal? TotalCost, double? AvgLatencyMs); +/// +/// Data transfer object representing a evaluator pass rate point. +/// public record EvaluatorPassRatePointDto( DateTimeOffset BucketStart, int Passed, int Total); +/// +/// Data transfer object representing a evaluator score bucket. +/// public record EvaluatorScoreBucketDto( string Score, int Count); +/// +/// Data transfer object representing a evaluator cost point. +/// public record EvaluatorCostPointDto( DateTimeOffset BucketStart, long InputTokens, @@ -150,12 +213,18 @@ public record EvaluatorCostPointDto( decimal Cost, double AvgLatencyMs); +/// +/// Data transfer object representing a evaluator overview. +/// public record EvaluatorOverviewDto( EvaluatorSummaryDto Summary, IReadOnlyList PassRateTrend, IReadOnlyList ScoreDistribution, IReadOnlyList CostTrend); +/// +/// Data transfer object representing a evaluator sparkline. +/// public record EvaluatorSparklineDto( Guid EvaluatorId, IReadOnlyList Points); diff --git a/Proxytrace.Api/Dto/TestCases/TestCaseProposalDtos.cs b/Proxytrace.Api/Dto/TestCases/TestCaseProposalDtos.cs index 37e3421f1..dc43403b4 100644 --- a/Proxytrace.Api/Dto/TestCases/TestCaseProposalDtos.cs +++ b/Proxytrace.Api/Dto/TestCases/TestCaseProposalDtos.cs @@ -3,8 +3,14 @@ namespace Proxytrace.Api.Dto.TestCases; +/// +/// Request payload for synthesize test cases operations. +/// public record SynthesizeTestCasesRequest( Guid? SuiteId = null, + /// + /// Data transfer object representing a synthesis round. + /// [StringLength(2000)] string? Instruction = null, [MaxLength(TestCaseProposalSet.MaxRounds)] IReadOnlyList? Rounds = null); @@ -12,10 +18,19 @@ public record SynthesisRoundDto( [StringLength(2000)] string? Instruction, TestCaseProposalSetDto Proposals); +/// +/// Data transfer object representing a proposed tool request. +/// public record ProposedToolRequestDto(string Name, string Arguments); +/// +/// Data transfer object representing a proposed expected output. +/// public record ProposedExpectedOutputDto(string Content, IReadOnlyList ToolRequests); +/// +/// Data transfer object representing a test case proposal. +/// public record TestCaseProposalDto( Guid AgentCallId, ProposalKind Kind, @@ -25,14 +40,23 @@ public record TestCaseProposalDto( ProposedExpectedOutputDto? ExpectedOutput, IReadOnlyList Flags); +/// +/// Data transfer object representing a skipped turn. +/// public record SkippedTurnDto(Guid AgentCallId, string Reason); +/// +/// Data transfer object representing a evaluator suggestion. +/// public record EvaluatorSuggestionDto( string Name, string Instructions, string Reason, EvaluatorSuggestionTarget Target); +/// +/// Data transfer object representing a test case proposal set. +/// public record TestCaseProposalSetDto( string Summary, IReadOnlyList Proposals, @@ -45,6 +69,9 @@ public record TestCaseProposalSetDto( /// public sealed class TestCaseProposalDtoMapper { + /// + /// To dto. + /// public TestCaseProposalSetDto ToDto(TestCaseProposalSet set) => new( set.Summary, @@ -55,6 +82,9 @@ set.EvaluatorSuggestion is { } suggestion suggestion.Name, suggestion.Instructions, suggestion.Reason, suggestion.Target) : null); + /// + /// To domain. + /// public SynthesisRound ToDomain(SynthesisRoundDto dto) => new(dto.Instruction, ToDomain(dto.Proposals)); diff --git a/Proxytrace.Api/Dto/TestRuns/TestCaseFixtureDto.cs b/Proxytrace.Api/Dto/TestRuns/TestCaseFixtureDto.cs index 4430051b0..94271052b 100644 --- a/Proxytrace.Api/Dto/TestRuns/TestCaseFixtureDto.cs +++ b/Proxytrace.Api/Dto/TestRuns/TestCaseFixtureDto.cs @@ -1,5 +1,8 @@ namespace Proxytrace.Api.Dto.TestRuns; +/// +/// Data transfer object representing a test case fixture. +/// public record TestCaseFixtureDto( TestCaseInputDto Input, OutputValueDto Expected, @@ -9,16 +12,28 @@ public record TestCaseFixtureDto( EndpointUsageDto[] Endpoints ); +/// +/// Data transfer object representing a test case input. +/// public record TestCaseInputDto(TestCaseMessageDto[] Messages); +/// +/// Data transfer object representing a test case message. +/// public record TestCaseMessageDto( string Role, string Content, ToolRequestFixtureDto[] ToolRequests, string? ToolCallId); +/// +/// Data transfer object representing a tool request fixture. +/// public record ToolRequestFixtureDto(string Id, string Name, string Arguments); +/// +/// Data transfer object representing a output value. +/// public record OutputValueDto( string Kind, string? Content, @@ -27,23 +42,41 @@ public record OutputValueDto( object? Arguments ); +/// +/// Data transfer object representing a tool call info. +/// public record ToolCallInfoDto(string Name, object Arguments); +/// +/// Data transfer object representing a model request preview. +/// public record ModelRequestPreviewDto( string Model, RequestMessageDto[] Messages, RequestToolDto[] Tools); +/// +/// Data transfer object representing a request message. +/// public record RequestMessageDto( string Role, string? Content, RequestToolCallDto[] ToolCalls, string? ToolCallId); +/// +/// Data transfer object representing a request tool call. +/// public record RequestToolCallDto(string Id, string Name, string Arguments); +/// +/// Data transfer object representing a request tool. +/// public record RequestToolDto(string Name, string Description, object JsonSchema); +/// +/// Data transfer object representing a evaluator fixture result. +/// public record EvaluatorFixtureResultDto( string EvaluatorId, string EvaluatorKind, @@ -54,10 +87,19 @@ public record EvaluatorFixtureResultDto( string Note ); +/// +/// Data transfer object representing a breakdown item. +/// public record BreakdownItemDto(string K, string V, bool Match); +/// +/// Data transfer object representing a runtime breakdown. +/// public record RuntimeBreakdownDto(long Total, long Ttft, long Gen, long Tools, long? Judge); +/// +/// Data transfer object representing a endpoint usage. +/// public record EndpointUsageDto( string Id, string Label, diff --git a/Proxytrace.Api/Dto/TestRuns/TestRunDto.cs b/Proxytrace.Api/Dto/TestRuns/TestRunDto.cs index c34660c0e..62447919f 100644 --- a/Proxytrace.Api/Dto/TestRuns/TestRunDto.cs +++ b/Proxytrace.Api/Dto/TestRuns/TestRunDto.cs @@ -4,8 +4,14 @@ namespace Proxytrace.Api.Dto.TestRuns; +/// +/// Data transfer object representing a run evaluator. +/// public record RunEvaluatorDto(Guid Id, EvaluatorKind Kind, string Name); +/// +/// Data transfer object representing a evaluation result. +/// public record EvaluationResultDto( Guid EvaluatorId, EvaluatorKind EvaluatorKind, @@ -14,6 +20,9 @@ public record EvaluationResultDto( string? Reasoning, string? ErrorMessage); +/// +/// Data transfer object representing a test run. +/// public record TestRunDto( Guid Id, Guid GroupId, @@ -44,8 +53,14 @@ public record TestRunDto( DateTimeOffset CreatedAt, DateTimeOffset UpdatedAt); +/// +/// Data transfer object representing a test case row. +/// public record TestCaseRowDto(Guid Id, string Summary); +/// +/// Data transfer object representing a test result. +/// public record TestResultDto( Guid Id, Guid TestCaseId, @@ -58,8 +73,14 @@ public record TestResultDto( long? TokensOut, long? CachedTokensIn); +/// +/// Data transfer object representing a test run message. +/// public record TestRunMessageDto(string Role, string Content); +/// +/// Data transfer object representing a test run group. +/// public record TestRunGroupDto( Guid Id, Guid SuiteId, @@ -109,6 +130,9 @@ public record TestRunGroupListItemDto( DateTimeOffset CreatedAt, DateTimeOffset UpdatedAt); +/// +/// Request payload for create test run group operations. +/// public record CreateTestRunGroupRequest( Guid TestSuiteId, IReadOnlyList ModelEndpointIds, diff --git a/Proxytrace.Api/Dto/TestRuns/TestRunDtoMapper.cs b/Proxytrace.Api/Dto/TestRuns/TestRunDtoMapper.cs index 58e3409f0..f4a2b2222 100644 --- a/Proxytrace.Api/Dto/TestRuns/TestRunDtoMapper.cs +++ b/Proxytrace.Api/Dto/TestRuns/TestRunDtoMapper.cs @@ -15,6 +15,9 @@ namespace Proxytrace.Api.Dto.TestRuns; /// public sealed class TestRunDtoMapper { + /// + /// To dto. + /// public TestRunDto ToDto(ITestRun r) { // A case passes only when it has been judged (≥1 evaluation) and every evaluation passed. @@ -128,6 +131,9 @@ public async Task ToListItemDtoAsync( UpdatedAt: group.UpdatedAt); } + /// + /// To fixture dto. + /// public TestCaseFixtureDto ToFixtureDto(ITestRun run, ITestResult result) => new( Input: new TestCaseInputDto(MapInputMessages(result.TestCase.Input)), @@ -137,6 +143,9 @@ public TestCaseFixtureDto ToFixtureDto(ITestRun run, ITestResult result) Runtime: MapRuntime(result), Endpoints: MapEndpoints(run, result)); + /// + /// To request dto. + /// public ModelRequestPreviewDto ToRequestDto(ModelRequestPreview preview) => new( preview.Model, diff --git a/Proxytrace.Api/Dto/TestRuns/TestRunScheduleDto.cs b/Proxytrace.Api/Dto/TestRuns/TestRunScheduleDto.cs index 3a58556c9..4f1d72572 100644 --- a/Proxytrace.Api/Dto/TestRuns/TestRunScheduleDto.cs +++ b/Proxytrace.Api/Dto/TestRuns/TestRunScheduleDto.cs @@ -1,7 +1,13 @@ namespace Proxytrace.Api.Dto.TestRuns; +/// +/// Data transfer object representing a schedule endpoint. +/// public record ScheduleEndpointDto(Guid Id, string Name); +/// +/// Data transfer object representing a test run schedule. +/// public record TestRunScheduleDto( Guid Id, string Name, Guid SuiteId, string SuiteName, Guid AgentId, string AgentName, IReadOnlyList Endpoints, int IntervalMinutes, bool IsEnabled, @@ -15,6 +21,9 @@ public record CreateTestRunScheduleRequest( string Name, Guid TestSuiteId, IReadOnlyList ModelEndpointIds, int IntervalMinutes, bool Enabled, DateTimeOffset? AnchorAt = null); +/// +/// Request payload for update test run schedule operations. +/// public record UpdateTestRunScheduleRequest( string Name, IReadOnlyList ModelEndpointIds, int IntervalMinutes, bool Enabled, DateTimeOffset? AnchorAt = null); diff --git a/Proxytrace.Api/Dto/TestSuites/TestSuiteDto.cs b/Proxytrace.Api/Dto/TestSuites/TestSuiteDto.cs index 359af1b33..e5cec25ad 100644 --- a/Proxytrace.Api/Dto/TestSuites/TestSuiteDto.cs +++ b/Proxytrace.Api/Dto/TestSuites/TestSuiteDto.cs @@ -3,6 +3,9 @@ namespace Proxytrace.Api.Dto.TestSuites; +/// +/// Data transfer object representing a test suite. +/// public record TestSuiteDto( Guid Id, string Name, @@ -45,6 +48,9 @@ public record TestSuiteListItemDto( DateTimeOffset CreatedAt, DateTimeOffset UpdatedAt); +/// +/// Data transfer object representing a evaluator. +/// public record EvaluatorDto(Guid Id, EvaluatorKind Kind); /// @@ -58,6 +64,9 @@ public record SuiteRunStatsDto( double? AvgDurationMs, decimal? TotalCost); +/// +/// Data transfer object representing a test case. +/// public record TestCaseDto( Guid Id, IReadOnlyList Input, @@ -72,17 +81,32 @@ public record TestCaseDto( // expected output that contradicts it can ever pass. See docs/optimization-loop.md. int ResolvedToolCallCount = 0); +/// +/// Data transfer object representing a test suite message. +/// public record TestSuiteMessageDto( string Role, string Content, IReadOnlyList? ToolRequests = null, string? ToolCallId = null); +/// +/// Data transfer object representing a tool request input. +/// public record ToolRequestInputDto(string Name, string Arguments, string? Id = null); +/// +/// Request payload for update test case operations. +/// public record UpdateTestCaseRequest(TestSuiteMessageDto ExpectedOutput); +/// +/// Request payload for create test suite operations. +/// public record CreateTestSuiteRequest( + /// + /// Request payload for create test case operations. + /// [Required, StringLength(200, MinimumLength = 1)] string Name, [Required] Guid AgentId, [Required, MaxLength(RequestLimits.MaxTestCases)] IReadOnlyList TestCases, @@ -93,6 +117,9 @@ public record CreateTestCaseRequest( IReadOnlyList? Input, TestSuiteMessageDto? ExpectedOutput); +/// +/// Request payload for add test case operations. +/// public record AddTestCaseRequest( Guid? FromAgentCallId, IReadOnlyList? Input, diff --git a/Proxytrace.Api/Dto/TestSuites/TestSuiteDtoMapper.cs b/Proxytrace.Api/Dto/TestSuites/TestSuiteDtoMapper.cs index bb2550bb0..387cd1e66 100644 --- a/Proxytrace.Api/Dto/TestSuites/TestSuiteDtoMapper.cs +++ b/Proxytrace.Api/Dto/TestSuites/TestSuiteDtoMapper.cs @@ -115,6 +115,9 @@ private readonly record struct RunAggregates( DateTimeOffset? LastRunAt, Guid? LastRunGroupId) { + /// + /// From. + /// public static RunAggregates From(IReadOnlyList runRows) { // One point per (group, endpoint) cohort — a sampled run counts as one run, and its @@ -130,6 +133,9 @@ public static RunAggregates From(IReadOnlyList runRows) } } + /// + /// Builds the conversation. + /// public Conversation BuildConversation(IReadOnlyList messages) { var msgs = new List(); @@ -147,6 +153,9 @@ public Conversation BuildConversation(IReadOnlyList message return new Conversation(msgs); } + /// + /// Builds the assistant message. + /// public AssistantMessage BuildAssistantMessage(TestSuiteMessageDto m) { IReadOnlyList contents = string.IsNullOrEmpty(m.Content) diff --git a/Proxytrace.Api/Dto/TestSuites/UpdateTestSuiteRequest.cs b/Proxytrace.Api/Dto/TestSuites/UpdateTestSuiteRequest.cs index afb96a1f9..0aa702b9d 100644 --- a/Proxytrace.Api/Dto/TestSuites/UpdateTestSuiteRequest.cs +++ b/Proxytrace.Api/Dto/TestSuites/UpdateTestSuiteRequest.cs @@ -2,6 +2,9 @@ namespace Proxytrace.Api.Dto.TestSuites; +/// +/// Request payload for update test suite operations. +/// public record UpdateTestSuiteRequest( Guid? AgentId, [MaxLength(RequestLimits.MaxEvaluators)] IReadOnlyList? EvaluatorIds, diff --git a/Proxytrace.Api/Dto/Theories/TheoryDtoMapper.cs b/Proxytrace.Api/Dto/Theories/TheoryDtoMapper.cs index 77d281f54..0755fae54 100644 --- a/Proxytrace.Api/Dto/Theories/TheoryDtoMapper.cs +++ b/Proxytrace.Api/Dto/Theories/TheoryDtoMapper.cs @@ -11,11 +11,17 @@ public sealed class TheoryDtoMapper { private readonly ToolDtoMapper toolDtoMapper; + /// + /// Initializes a new instance of the class. + /// public TheoryDtoMapper(ToolDtoMapper toolDtoMapper) { this.toolDtoMapper = toolDtoMapper; } + /// + /// To dto. + /// public TheoryDto ToDto(IOptimizationTheory t) => new( t.Id, diff --git a/Proxytrace.Api/Dto/Tools/ToolDtoMapper.cs b/Proxytrace.Api/Dto/Tools/ToolDtoMapper.cs index 8f8b2bd3c..3b1a4de88 100644 --- a/Proxytrace.Api/Dto/Tools/ToolDtoMapper.cs +++ b/Proxytrace.Api/Dto/Tools/ToolDtoMapper.cs @@ -10,9 +10,15 @@ namespace Proxytrace.Api.Dto.Tools; /// public sealed class ToolDtoMapper { + /// + /// To tool spec dto. + /// public ToolSpecificationDto ToToolSpecDto(ToolSpecification t) => new(t.Name, t.Description, [.. t.Arguments.Arguments.Select(ToToolArgumentDto)]); + /// + /// To tool argument dto. + /// public ToolArgumentDto ToToolArgumentDto(IToolArgument arg) { var (type, enumValues) = ParseJsonSchema(arg.JsonSchema); diff --git a/Proxytrace.Api/Dto/Users/UserDto.cs b/Proxytrace.Api/Dto/Users/UserDto.cs index 9eaa41389..4c6229e28 100644 --- a/Proxytrace.Api/Dto/Users/UserDto.cs +++ b/Proxytrace.Api/Dto/Users/UserDto.cs @@ -3,6 +3,9 @@ namespace Proxytrace.Api.Dto.Users; +/// +/// Data transfer object representing a user. +/// public record UserDto( Guid Id, string Email, @@ -12,6 +15,9 @@ public record UserDto( DateTimeOffset UpdatedAt, bool MfaEnabled); +/// +/// Request payload for update user role operations. +/// public record UpdateUserRoleRequest(UserRole Role); /// Self-service UI language change for the current user (BCP-47 culture code). diff --git a/Proxytrace.Api/Evaluators/EvaluatorBuilder.cs b/Proxytrace.Api/Evaluators/EvaluatorBuilder.cs index 311df6e3c..c477be66e 100644 --- a/Proxytrace.Api/Evaluators/EvaluatorBuilder.cs +++ b/Proxytrace.Api/Evaluators/EvaluatorBuilder.cs @@ -33,6 +33,9 @@ public sealed class EvaluatorBuilder private readonly IJsonSchemaMatchEvaluator.CreateExisting createJsonSchemaMatchExisting; private readonly ILicenseService licenseService; + /// + /// Initializes a new instance of the class. + /// public EvaluatorBuilder( IAgent.CreateNew createAgent, IModelParameters.Create createModelParameters, @@ -67,6 +70,9 @@ private void EnsureAgenticLicensed() throw new FeatureNotLicensedException(LicenseFeature.AgenticEvaluators, licenseService.Current.Tier); } + /// + /// Builds asynchronously. + /// public Task BuildAsync( CreateEvaluatorRequest request, IProject project, @@ -81,6 +87,9 @@ public Task BuildAsync( _ => throw new ArgumentOutOfRangeException(nameof(request), request.GetType().Name), }; + /// + /// Builds asynchronously. + /// public Task BuildAsync( UpdateEvaluatorRequest request, IEvaluator existing, diff --git a/Proxytrace.Api/IngestionProxyOptions.cs b/Proxytrace.Api/IngestionProxyOptions.cs index 0b2b64ce6..b2a8522de 100644 --- a/Proxytrace.Api/IngestionProxyOptions.cs +++ b/Proxytrace.Api/IngestionProxyOptions.cs @@ -8,5 +8,8 @@ namespace Proxytrace.Api; /// public sealed record IngestionProxyOptions { + /// + /// Gets or sets the public base url. + /// public string? PublicBaseUrl { get; init; } } diff --git a/Proxytrace.Api/Json/ApiJsonOptions.cs b/Proxytrace.Api/Json/ApiJsonOptions.cs index 1fad4ea05..c1b947bb1 100644 --- a/Proxytrace.Api/Json/ApiJsonOptions.cs +++ b/Proxytrace.Api/Json/ApiJsonOptions.cs @@ -9,6 +9,9 @@ namespace Proxytrace.Api.Json; /// internal static class ApiJsonOptions { + /// + /// The sse. + /// public static readonly JsonSerializerOptions Sse = new() { PropertyNamingPolicy = JsonNamingPolicy.CamelCase, diff --git a/Proxytrace.Api/Mcp/McpProjectAccessor.cs b/Proxytrace.Api/Mcp/McpProjectAccessor.cs index 58975dd93..58380e139 100644 --- a/Proxytrace.Api/Mcp/McpProjectAccessor.cs +++ b/Proxytrace.Api/Mcp/McpProjectAccessor.cs @@ -22,12 +22,18 @@ internal sealed class McpProjectAccessor : IMcpProjectAccessor private readonly IHttpContextAccessor httpContextAccessor; private readonly IProjectRepository projects; + /// + /// Initializes a new instance of the class. + /// public McpProjectAccessor(IHttpContextAccessor httpContextAccessor, IProjectRepository projects) { this.httpContextAccessor = httpContextAccessor; this.projects = projects; } + /// + /// Gets the project asynchronously. + /// public async Task GetProjectAsync(CancellationToken cancellationToken = default) { var ctx = httpContextAccessor.HttpContext @@ -50,6 +56,9 @@ public async Task GetProjectAsync(CancellationToken cancellationToken return project; } + /// + /// Require write scope. + /// public void RequireWriteScope() { var ctx = httpContextAccessor.HttpContext diff --git a/Proxytrace.Api/Mcp/Prompts/ProxytracePrompts.cs b/Proxytrace.Api/Mcp/Prompts/ProxytracePrompts.cs index f4d365649..9ef0d6525 100644 --- a/Proxytrace.Api/Mcp/Prompts/ProxytracePrompts.cs +++ b/Proxytrace.Api/Mcp/Prompts/ProxytracePrompts.cs @@ -15,6 +15,9 @@ internal static class ProxytracePrompts private static string Target(string? subject, string label) => string.IsNullOrWhiteSpace(subject) ? string.Empty : $"\n\n{label}: {subject}"; + /// + /// Optimize agent. + /// [McpServerPrompt(Name = "optimize_agent")] [Description("Guided loop: gather evidence from runs and traces, then submit ONE A/B-tested optimization theory for an agent.")] public static string OptimizeAgent( @@ -63,6 +66,9 @@ Author a full rewritten system message (read the current one from `get_agent`, n and a suite.{{Target(agent, "Target agent")}} """; + /// + /// Curate suite. + /// [McpServerPrompt(Name = "curate_suite")] [Description("Build or grow a benchmark test suite from captured traces for an agent.")] public static string CurateSuite( @@ -85,6 +91,9 @@ 4. Confirm with `get_suite` and summarize what the suite now covers. Guardrails: only promote traces from the current project; never invent trace ids.{{Target(agent, "Target agent")}} """; + /// + /// Runs the tests. + /// [McpServerPrompt(Name = "run_tests")] [Description("Run a test suite against its agent and review the results.")] public static string RunTests( @@ -103,6 +112,9 @@ 3. Poll `get_test_run` on the returned group id until its status is terminal the `optimize_agent` workflow.{{Target(suite, "Target suite")}} """; + /// + /// Review proposals. + /// [McpServerPrompt(Name = "review_proposals")] [Description("Review open optimization proposals and approve, reject, or mark them adopted.")] public static string ReviewProposals() => @@ -125,6 +137,9 @@ public static string ReviewProposals() => Guardrails: act only on proposals in the current project; explain each decision in one line. """; + /// + /// Project insights. + /// [McpServerPrompt(Name = "project_insights")] [Description("Survey the project's health: usage, cost, pass rates, and notable traces.")] public static string ProjectInsights() => diff --git a/Proxytrace.Api/Mcp/Tools/AgentTools.cs b/Proxytrace.Api/Mcp/Tools/AgentTools.cs index bf2929b48..984de799a 100644 --- a/Proxytrace.Api/Mcp/Tools/AgentTools.cs +++ b/Proxytrace.Api/Mcp/Tools/AgentTools.cs @@ -18,6 +18,9 @@ internal sealed class AgentTools private readonly IAgentCallRepository agentCalls; private readonly AgentDtoMapper mapper; + /// + /// Initializes a new instance of the class. + /// public AgentTools( IMcpProjectAccessor project, IAgentRepository agents, @@ -33,6 +36,9 @@ public AgentTools( [McpServerTool(Name = "list_agents")] [Description("List the agents in the current project (the project the API key belongs to). " + "Returns id, name, model endpoint and tool count for each, most recently used first.")] + /// + /// Lists the agents. + /// public async Task> ListAgents(CancellationToken cancellationToken) { var p = await project.GetProjectAsync(cancellationToken); @@ -49,6 +55,9 @@ public async Task> ListAgents(CancellationToken [McpServerTool(Name = "get_agent")] [Description("Get a single agent by id, including its full system prompt and tool specifications. " + "The agent must belong to the current project.")] + /// + /// Gets the agent. + /// public async Task GetAgent( [Description("The agent id (GUID), as returned by list_agents.")] Guid agentId, CancellationToken cancellationToken) diff --git a/Proxytrace.Api/Mcp/Tools/ProposalTools.cs b/Proxytrace.Api/Mcp/Tools/ProposalTools.cs index 0234a4205..c82a07c3c 100644 --- a/Proxytrace.Api/Mcp/Tools/ProposalTools.cs +++ b/Proxytrace.Api/Mcp/Tools/ProposalTools.cs @@ -25,6 +25,9 @@ internal sealed class ProposalTools private readonly ILicenseService license; private readonly ILogger audit; + /// + /// Initializes a new instance of the class. + /// public ProposalTools( IMcpProjectAccessor project, IOptimizationProposalRepository repository, @@ -44,6 +47,9 @@ public ProposalTools( [McpServerTool(Name = "list_proposals")] [Description("List the optimization proposals in the current project (most recent first), with their " + "kind, status and expected pass-rate delta.")] + /// + /// Lists the proposals. + /// public async Task> ListProposals(CancellationToken cancellationToken) { EnsureFeature(); @@ -52,6 +58,9 @@ public async Task> ListProposals(Cancella return proposals.Select(mapper.ToDto).ToArray(); } + /// + /// Gets the proposal. + /// [McpServerTool(Name = "get_proposal")] [Description("Get a single optimization proposal by id. It must belong to the current project.")] public async Task GetProposal( @@ -65,6 +74,9 @@ public async Task GetProposal( [McpServerTool(Name = "get_proposal_artifact")] [Description("Get the machine-readable handoff package for applying a proposal's change to the agent's " + "actual implementation. The proposal must belong to the current project.")] + /// + /// Gets the proposal artifact. + /// public async Task GetProposalArtifact( [Description("The proposal id (GUID), from list_proposals.")] Guid proposalId, CancellationToken cancellationToken) @@ -76,6 +88,9 @@ public async Task GetProposalArtifact( [McpServerTool(Name = "set_proposal_status")] [Description("Approve, reject or mark-adopted an optimization proposal. Valid transitions: Draft->Accepted, " + "Draft->Rejected, Accepted->Adopted. The proposal must belong to the current project.")] + /// + /// Sets the proposal status. + /// public async Task SetProposalStatus( [Description("The proposal id (GUID), from list_proposals.")] Guid proposalId, [Description("The target status: Accepted, Rejected or Adopted.")] ProposalStatus status, diff --git a/Proxytrace.Api/Mcp/Tools/RunTools.cs b/Proxytrace.Api/Mcp/Tools/RunTools.cs index 1814abd6d..a43e0b3e6 100644 --- a/Proxytrace.Api/Mcp/Tools/RunTools.cs +++ b/Proxytrace.Api/Mcp/Tools/RunTools.cs @@ -41,6 +41,9 @@ internal sealed class RunTools private readonly TestRunDtoMapper mapper; private readonly ILogger audit; + /// + /// Initializes a new instance of the class. + /// public RunTools( IMcpProjectAccessor project, ITestRunGroupRepository groups, @@ -62,6 +65,9 @@ public RunTools( [McpServerTool(Name = "list_test_runs")] [Description("List test runs (run groups) in the current project, newest first, with per-run status " + "and pass/fail counts. System runs are excluded.")] + /// + /// Lists the test runs. + /// public async Task> ListTestRuns( [Description("Maximum number of runs to return (1-100, default 50).")] int limit = 50, CancellationToken cancellationToken = default) @@ -76,6 +82,9 @@ public async Task> ListTestRuns( [McpServerTool(Name = "get_test_run")] [Description("Get a single test run (run group) by id, including each run's status and results. " + "The run must belong to the current project.")] + /// + /// Gets the test run. + /// public async Task GetTestRun( [Description("The run group id (GUID), as returned by list_test_runs or start_test_run.")] Guid runGroupId, CancellationToken cancellationToken) @@ -88,6 +97,9 @@ public async Task GetTestRun( [Description("Start a test run of a suite against its agent's current endpoint. Returns the run group; " + "poll get_test_run for progress. The suite must belong to the current project. NOTE: a run " + "makes real LLM calls and incurs cost.")] + /// + /// Starts the test run. + /// public async Task StartTestRun( [Description("The suite id (GUID) to run, from list_suites.")] Guid suiteId, CancellationToken cancellationToken) @@ -104,6 +116,9 @@ public async Task StartTestRun( return await ToDtoAsync(group, cancellationToken); } + /// + /// Cancels the test run. + /// [McpServerTool(Name = "cancel_test_run")] [Description("Cancel an in-progress test run (run group). The run must belong to the current project.")] public async Task CancelTestRun( @@ -120,6 +135,9 @@ public async Task CancelTestRun( [Description("Analyze the failing cases of a single run, with each evaluator's verdict and reasoning and " + "the actual response — the primary evidence for an optimization theory. Pass a run id from a " + "run group's `runs` (get_test_run). The run must belong to the current project.")] + /// + /// Gets the run failures. + /// public async Task> GetRunFailures( [Description("The run id (GUID) — a single run from a run group's `runs`, via get_test_run.")] Guid runId, [Description("Maximum number of failing cases to return (1-50, default 20).")] int limit = 20, @@ -143,6 +161,9 @@ public async Task> GetRunFailures( [McpServerTool(Name = "compare_runs")] [Description("Compare two runs of the same suite case-by-case: which cases were fixed (fail→pass), which " + "regressed (pass→fail), and the pass rates. Both runs must belong to the current project.")] + /// + /// Compares the runs. + /// public async Task CompareRuns( [Description("The baseline run id (GUID).")] Guid baselineRunId, [Description("The new run id (GUID) to compare against the baseline.")] Guid newRunId, diff --git a/Proxytrace.Api/Mcp/Tools/StatsTools.cs b/Proxytrace.Api/Mcp/Tools/StatsTools.cs index 3b9f0b1a7..ca25227bf 100644 --- a/Proxytrace.Api/Mcp/Tools/StatsTools.cs +++ b/Proxytrace.Api/Mcp/Tools/StatsTools.cs @@ -28,6 +28,9 @@ internal sealed class StatsTools private readonly IAgentStatistics agentStatistics; private readonly IAgentRepository agents; + /// + /// Initializes a new instance of the class. + /// public StatsTools( IMcpProjectAccessor project, IDashboardStatistics dashboard, @@ -43,6 +46,9 @@ public StatsTools( [McpServerTool(Name = "get_dashboard")] [Description("Get project-wide usage statistics: total calls, token usage, average latency, overall " + "pass rate, and a per-model breakdown. Optionally bound the window with from/to (ISO-8601).")] + /// + /// Gets the dashboard. + /// public async Task GetDashboard( [Description("Optional start of the window (ISO-8601). Omit for all-time.")] DateTimeOffset? from = null, [Description("Optional end of the window (ISO-8601). Omit for all-time.")] DateTimeOffset? to = null, @@ -72,6 +78,9 @@ public async Task GetDashboard( [Description("Get an agent's overview over a window (default last 30 days): token/cost/latency summary, " + "daily time series, pass-rate trend, per-suite pass rates and entity counts. Use it to motivate " + "an optimization (e.g. a model switch). The agent must belong to the current project.")] + /// + /// Gets the agent overview. + /// public async Task GetAgentOverview( [Description("The agent id (GUID), from list_agents.")] Guid agentId, [Description("Optional window start (ISO-8601). Defaults to 30 days before the end.")] DateTimeOffset? from = null, diff --git a/Proxytrace.Api/Mcp/Tools/SuiteTools.cs b/Proxytrace.Api/Mcp/Tools/SuiteTools.cs index f1f20687b..e08815695 100644 --- a/Proxytrace.Api/Mcp/Tools/SuiteTools.cs +++ b/Proxytrace.Api/Mcp/Tools/SuiteTools.cs @@ -35,6 +35,9 @@ internal sealed class SuiteTools private readonly ILicenseService license; private readonly ILogger audit; + /// + /// Initializes a new instance of the class. + /// public SuiteTools( IMcpProjectAccessor project, ITestSuiteRepository suites, @@ -67,6 +70,9 @@ public SuiteTools( this.audit = audit; } + /// + /// Lists the suites. + /// [McpServerTool(Name = "list_suites")] [Description("List the test suites in the current project, with each suite's test-case count.")] public async Task> ListSuites( @@ -82,6 +88,9 @@ public async Task> ListSuites( [McpServerTool(Name = "get_suite")] [Description("Get a single test suite by id, including its test cases and evaluators. " + "The suite must belong to the current project.")] + /// + /// Gets the suite. + /// public async Task GetSuite( [Description("The suite id (GUID), as returned by list_suites.")] Guid suiteId, CancellationToken cancellationToken) @@ -94,6 +103,9 @@ public async Task GetSuite( [Description("Create a benchmark test suite by promoting captured traces: each trace becomes a test " + "case whose expected output is the response recorded during that call. The agent must " + "belong to the current project. A default exact-match evaluator is attached.")] + /// + /// Creates the suite from traces. + /// public async Task CreateSuiteFromTraces( [Description("Name for the new suite.")] string name, [Description("The agent id (GUID) the suite benchmarks, from list_agents.")] Guid agentId, @@ -146,6 +158,9 @@ public async Task CreateSuiteFromTraces( "output contradicting that result can never pass. Check the new case's " + "resolvedToolCallCount in the returned suite — anything above 0 means you graded a " + "summary; correct the earlier trace whose response contains the wrong tool call.")] + /// + /// Adds the trace to suite. + /// public async Task AddTraceToSuite( [Description("The suite id (GUID), from list_suites.")] Guid suiteId, [Description("The trace id (GUID) to add, from list_traces.")] Guid traceId, diff --git a/Proxytrace.Api/Mcp/Tools/TheoryTools.cs b/Proxytrace.Api/Mcp/Tools/TheoryTools.cs index 0cb911044..28db9c346 100644 --- a/Proxytrace.Api/Mcp/Tools/TheoryTools.cs +++ b/Proxytrace.Api/Mcp/Tools/TheoryTools.cs @@ -48,6 +48,9 @@ internal sealed class TheoryTools private readonly ILicenseService license; private readonly ILogger audit; + /// + /// Initializes a new instance of the class. + /// public TheoryTools( IMcpProjectAccessor project, IOptimizationTheoryRepository repository, @@ -79,6 +82,9 @@ public TheoryTools( [McpServerTool(Name = "list_theories")] [Description("List optimization theories in the current project, with each one's status, rationale and " + "A/B validation outcome. Optionally filter by status.")] + /// + /// Lists the theories. + /// public async Task> ListTheories( [Description("Optional status filter: Proposed, Validating, Validated or Invalidated.")] TheoryStatus? status = null, CancellationToken cancellationToken = default) @@ -91,6 +97,9 @@ public async Task> ListTheories( return theories.Select(mapper.ToDto).ToArray(); } + /// + /// Gets the theory. + /// [McpServerTool(Name = "get_theory")] [Description("Get a single optimization theory by id. It must belong to the current project.")] public async Task GetTheory( @@ -110,6 +119,9 @@ public async Task GetTheory( "by a background baseline-vs-candidate A/B run against a suite. On a win it becomes a reviewable " + "proposal; otherwise it's invalidated. Poll get_theory for the outcome. Choose exactly one " + "`kind` and fill its field. Agent and suite must belong to the current project.")] + /// + /// Submit theory. + /// public async Task SubmitTheory( [Description("The agent id (GUID) to optimize, from list_agents.")] Guid agentId, [Description("The suite id (GUID) to validate against, from list_suites.")] Guid suiteId, diff --git a/Proxytrace.Api/Mcp/Tools/TraceTools.cs b/Proxytrace.Api/Mcp/Tools/TraceTools.cs index f9915a7e9..6638b300f 100644 --- a/Proxytrace.Api/Mcp/Tools/TraceTools.cs +++ b/Proxytrace.Api/Mcp/Tools/TraceTools.cs @@ -16,6 +16,9 @@ internal sealed class TraceTools private readonly IAgentCallRepository calls; private readonly AgentCallDtoMapper mapper; + /// + /// Initializes a new instance of the class. + /// public TraceTools(IMcpProjectAccessor project, IAgentCallRepository calls, AgentCallDtoMapper mapper) { this.project = project; @@ -26,6 +29,9 @@ public TraceTools(IMcpProjectAccessor project, IAgentCallRepository calls, Agent [McpServerTool(Name = "list_traces")] [Description("Search captured LLM traces (agent calls) in the current project, newest first. " + "Optionally filter by agent, a free-text query, or HTTP status.")] + /// + /// Lists the traces. + /// public async Task> ListTraces( [Description("Optional agent id (GUID) to restrict the search to a single agent.")] Guid? agentId = null, [Description("Optional free-text query matched against the captured request/response.")] string? query = null, @@ -43,6 +49,9 @@ public async Task> ListTraces( [McpServerTool(Name = "get_trace")] [Description("Get a single captured trace (agent call) by id, including the full request, response and tools. " + "The trace must belong to the current project.")] + /// + /// Gets the trace. + /// public async Task GetTrace( [Description("The trace id (GUID), as returned by list_traces.")] Guid traceId, CancellationToken cancellationToken) diff --git a/Proxytrace.Api/Middleware/AuditDeniedAccessMiddleware.cs b/Proxytrace.Api/Middleware/AuditDeniedAccessMiddleware.cs index a94307956..e42071bd8 100644 --- a/Proxytrace.Api/Middleware/AuditDeniedAccessMiddleware.cs +++ b/Proxytrace.Api/Middleware/AuditDeniedAccessMiddleware.cs @@ -32,12 +32,18 @@ internal sealed class AuditDeniedAccessMiddleware private readonly RequestDelegate next; private readonly ILogger audit; + /// + /// Initializes a new instance of the class. + /// public AuditDeniedAccessMiddleware(RequestDelegate next, ILogger audit) { this.next = next; this.audit = audit; } + /// + /// Invokes asynchronously. + /// public async Task InvokeAsync(HttpContext context) { await next(context); diff --git a/Proxytrace.Api/Middleware/ExceptionHandlingMiddleware.cs b/Proxytrace.Api/Middleware/ExceptionHandlingMiddleware.cs index 39b14bfdf..e0e2544ff 100644 --- a/Proxytrace.Api/Middleware/ExceptionHandlingMiddleware.cs +++ b/Proxytrace.Api/Middleware/ExceptionHandlingMiddleware.cs @@ -12,6 +12,9 @@ internal sealed class ExceptionHandlingMiddleware private readonly IEnumerable mappers; private readonly bool isDevelopment; + /// + /// Initializes a new instance of the class. + /// public ExceptionHandlingMiddleware( RequestDelegate next, ILogger logger, @@ -24,6 +27,9 @@ public ExceptionHandlingMiddleware( this.isDevelopment = env.IsDevelopment(); } + /// + /// Invokes asynchronously. + /// public async Task InvokeAsync(HttpContext context) { try diff --git a/Proxytrace.Api/Middleware/Exceptions/DbUpdateExceptionMapper.cs b/Proxytrace.Api/Middleware/Exceptions/DbUpdateExceptionMapper.cs index e9b90335f..4d79c8381 100644 --- a/Proxytrace.Api/Middleware/Exceptions/DbUpdateExceptionMapper.cs +++ b/Proxytrace.Api/Middleware/Exceptions/DbUpdateExceptionMapper.cs @@ -15,8 +15,14 @@ internal sealed class DbUpdateExceptionMapper : IExceptionMapper // PostgreSQL SQLSTATE class 23 = integrity constraint violation. private const string ForeignKeyViolation = "23503"; + /// + /// Determines whether the map. + /// public bool CanMap(Exception exception) => exception is DbUpdateException; + /// + /// Maps. + /// public ExceptionMapping Map(Exception exception) => new() { StatusCode = StatusCodes.Status409Conflict, diff --git a/Proxytrace.Api/Middleware/Exceptions/EntityConflictExceptionMapper.cs b/Proxytrace.Api/Middleware/Exceptions/EntityConflictExceptionMapper.cs index 5e254664b..891b7e741 100644 --- a/Proxytrace.Api/Middleware/Exceptions/EntityConflictExceptionMapper.cs +++ b/Proxytrace.Api/Middleware/Exceptions/EntityConflictExceptionMapper.cs @@ -4,9 +4,15 @@ namespace Proxytrace.Api.Middleware.Exceptions; internal sealed class EntityConflictExceptionMapper : IExceptionMapper { + /// + /// Determines whether the map. + /// public bool CanMap(Exception exception) => exception is EntityAlreadyExistsException or OptimisticConcurrencyException; + /// + /// Maps. + /// public ExceptionMapping Map(Exception exception) => new() { StatusCode = StatusCodes.Status409Conflict, diff --git a/Proxytrace.Api/Middleware/Exceptions/EntityNotFoundExceptionMapper.cs b/Proxytrace.Api/Middleware/Exceptions/EntityNotFoundExceptionMapper.cs index 9e76cb10b..dace28a94 100644 --- a/Proxytrace.Api/Middleware/Exceptions/EntityNotFoundExceptionMapper.cs +++ b/Proxytrace.Api/Middleware/Exceptions/EntityNotFoundExceptionMapper.cs @@ -4,9 +4,15 @@ namespace Proxytrace.Api.Middleware.Exceptions; internal sealed class EntityNotFoundExceptionMapper : IExceptionMapper { + /// + /// Determines whether the map. + /// public bool CanMap(Exception exception) => exception is EntityNotFoundException or EntitiesNotFoundException; + /// + /// Maps. + /// public ExceptionMapping Map(Exception exception) => new() { StatusCode = StatusCodes.Status404NotFound, diff --git a/Proxytrace.Api/Middleware/Exceptions/ExceptionMapping.cs b/Proxytrace.Api/Middleware/Exceptions/ExceptionMapping.cs index 6578eceb8..d9d75fa1b 100644 --- a/Proxytrace.Api/Middleware/Exceptions/ExceptionMapping.cs +++ b/Proxytrace.Api/Middleware/Exceptions/ExceptionMapping.cs @@ -8,8 +8,20 @@ namespace Proxytrace.Api.Middleware.Exceptions; /// internal sealed record ExceptionMapping { + /// + /// Gets or sets the status code. + /// public required int StatusCode { get; init; } + /// + /// Gets or sets the type name. + /// public required string TypeName { get; init; } + /// + /// Gets or sets the message. + /// public string? Message { get; init; } + /// + /// Gets or sets the additional fields. + /// public IReadOnlyDictionary? AdditionalFields { get; init; } } diff --git a/Proxytrace.Api/Middleware/Exceptions/FeatureNotLicensedExceptionMapper.cs b/Proxytrace.Api/Middleware/Exceptions/FeatureNotLicensedExceptionMapper.cs index 15a31588b..794a3742b 100644 --- a/Proxytrace.Api/Middleware/Exceptions/FeatureNotLicensedExceptionMapper.cs +++ b/Proxytrace.Api/Middleware/Exceptions/FeatureNotLicensedExceptionMapper.cs @@ -4,8 +4,14 @@ namespace Proxytrace.Api.Middleware.Exceptions; internal sealed class FeatureNotLicensedExceptionMapper : IExceptionMapper { + /// + /// Determines whether the map. + /// public bool CanMap(Exception exception) => exception is FeatureNotLicensedException; + /// + /// Maps. + /// public ExceptionMapping Map(Exception exception) { var feature = (FeatureNotLicensedException)exception; diff --git a/Proxytrace.Api/Middleware/Exceptions/InvalidLicenseExceptionMapper.cs b/Proxytrace.Api/Middleware/Exceptions/InvalidLicenseExceptionMapper.cs index 450678d87..fec2ea159 100644 --- a/Proxytrace.Api/Middleware/Exceptions/InvalidLicenseExceptionMapper.cs +++ b/Proxytrace.Api/Middleware/Exceptions/InvalidLicenseExceptionMapper.cs @@ -4,8 +4,14 @@ namespace Proxytrace.Api.Middleware.Exceptions; internal sealed class InvalidLicenseExceptionMapper : IExceptionMapper { + /// + /// Determines whether the map. + /// public bool CanMap(Exception exception) => exception is InvalidLicenseException; + /// + /// Maps. + /// public ExceptionMapping Map(Exception exception) { var invalid = (InvalidLicenseException)exception; diff --git a/Proxytrace.Api/Middleware/Exceptions/LicenseLimitExceededExceptionMapper.cs b/Proxytrace.Api/Middleware/Exceptions/LicenseLimitExceededExceptionMapper.cs index 969a80638..babb915d1 100644 --- a/Proxytrace.Api/Middleware/Exceptions/LicenseLimitExceededExceptionMapper.cs +++ b/Proxytrace.Api/Middleware/Exceptions/LicenseLimitExceededExceptionMapper.cs @@ -4,8 +4,14 @@ namespace Proxytrace.Api.Middleware.Exceptions; internal sealed class LicenseLimitExceededExceptionMapper : IExceptionMapper { + /// + /// Determines whether the map. + /// public bool CanMap(Exception exception) => exception is LicenseLimitExceededException; + /// + /// Maps. + /// public ExceptionMapping Map(Exception exception) { var limit = (LicenseLimitExceededException)exception; diff --git a/Proxytrace.Api/Middleware/Exceptions/MalformedEndpointUrlExceptionMapper.cs b/Proxytrace.Api/Middleware/Exceptions/MalformedEndpointUrlExceptionMapper.cs index 195da3465..410e899ce 100644 --- a/Proxytrace.Api/Middleware/Exceptions/MalformedEndpointUrlExceptionMapper.cs +++ b/Proxytrace.Api/Middleware/Exceptions/MalformedEndpointUrlExceptionMapper.cs @@ -4,11 +4,17 @@ namespace Proxytrace.Api.Middleware.Exceptions; internal sealed class MalformedEndpointUrlExceptionMapper : IExceptionMapper { + /// + /// Determines whether the map. + /// public bool CanMap(Exception exception) => exception is MalformedEndpointUrlException; // The message only echoes the user's own input plus a format hint — no internals — so it is // safe to surface as the 400 body. + /// + /// Maps. + /// public ExceptionMapping Map(Exception exception) => new() { StatusCode = StatusCodes.Status400BadRequest, diff --git a/Proxytrace.Api/Middleware/Exceptions/NotImplementedExceptionMapper.cs b/Proxytrace.Api/Middleware/Exceptions/NotImplementedExceptionMapper.cs index daff3cbf9..8158d841a 100644 --- a/Proxytrace.Api/Middleware/Exceptions/NotImplementedExceptionMapper.cs +++ b/Proxytrace.Api/Middleware/Exceptions/NotImplementedExceptionMapper.cs @@ -2,8 +2,14 @@ namespace Proxytrace.Api.Middleware.Exceptions; internal sealed class NotImplementedExceptionMapper : IExceptionMapper { + /// + /// Determines whether the map. + /// public bool CanMap(Exception exception) => exception is NotImplementedException; + /// + /// Maps. + /// public ExceptionMapping Map(Exception exception) => new() { StatusCode = StatusCodes.Status501NotImplemented, diff --git a/Proxytrace.Api/Middleware/Exceptions/UserAdministrationExceptionMapper.cs b/Proxytrace.Api/Middleware/Exceptions/UserAdministrationExceptionMapper.cs index 416ebc41a..896d92322 100644 --- a/Proxytrace.Api/Middleware/Exceptions/UserAdministrationExceptionMapper.cs +++ b/Proxytrace.Api/Middleware/Exceptions/UserAdministrationExceptionMapper.cs @@ -4,9 +4,15 @@ namespace Proxytrace.Api.Middleware.Exceptions; internal sealed class UserAdministrationExceptionMapper : IExceptionMapper { + /// + /// Determines whether the map. + /// public bool CanMap(Exception exception) => exception is UserAdministrationException; + /// + /// Maps. + /// public ExceptionMapping Map(Exception exception) => new() { StatusCode = StatusCodes.Status409Conflict, diff --git a/Proxytrace.Api/Middleware/KioskReadOnlyMiddleware.cs b/Proxytrace.Api/Middleware/KioskReadOnlyMiddleware.cs index a93d0fbcf..619361032 100644 --- a/Proxytrace.Api/Middleware/KioskReadOnlyMiddleware.cs +++ b/Proxytrace.Api/Middleware/KioskReadOnlyMiddleware.cs @@ -16,6 +16,9 @@ internal sealed class KioskReadOnlyMiddleware private readonly KioskOptions options; private readonly KioskEndpointOptions endpoint; + /// + /// Initializes a new instance of the class. + /// public KioskReadOnlyMiddleware(RequestDelegate next, KioskOptions options, KioskEndpointOptions endpoint) { this.next = next; @@ -23,6 +26,9 @@ public KioskReadOnlyMiddleware(RequestDelegate next, KioskOptions options, Kiosk this.endpoint = endpoint; } + /// + /// Invokes asynchronously. + /// public async Task InvokeAsync(HttpContext context) { if (!options.Enabled) diff --git a/Proxytrace.Api/Middleware/SecurityHeadersMiddleware.cs b/Proxytrace.Api/Middleware/SecurityHeadersMiddleware.cs index fbaba023e..11d5f17e3 100644 --- a/Proxytrace.Api/Middleware/SecurityHeadersMiddleware.cs +++ b/Proxytrace.Api/Middleware/SecurityHeadersMiddleware.cs @@ -10,12 +10,18 @@ internal sealed class SecurityHeadersMiddleware private readonly RequestDelegate next; private readonly SecurityHeadersOptions options; + /// + /// Initializes a new instance of the class. + /// public SecurityHeadersMiddleware(RequestDelegate next, SecurityHeadersOptions options) { this.next = next; this.options = options; } + /// + /// Invokes asynchronously. + /// public Task InvokeAsync(HttpContext context) { var headers = context.Response.Headers; diff --git a/Proxytrace.Api/Middleware/SecurityHeadersOptions.cs b/Proxytrace.Api/Middleware/SecurityHeadersOptions.cs index 2c3ab1dc2..92c168208 100644 --- a/Proxytrace.Api/Middleware/SecurityHeadersOptions.cs +++ b/Proxytrace.Api/Middleware/SecurityHeadersOptions.cs @@ -6,16 +6,25 @@ namespace Proxytrace.Api.Middleware; /// public sealed record SecurityHeadersOptions { + /// + /// Gets or sets the content security policy. + /// public string ContentSecurityPolicy { get; init; } = "default-src 'self'; script-src 'self'; style-src 'self' 'unsafe-inline'; " + "img-src 'self' data:; font-src 'self' data:; connect-src 'self' https:; " + "base-uri 'self'; form-action 'self'; object-src 'none'; frame-ancestors 'none'"; + /// + /// Gets or sets the docs content security policy. + /// public string DocsContentSecurityPolicy { get; init; } = "default-src 'self'; script-src 'self' 'unsafe-inline'; style-src 'self' 'unsafe-inline'; " + "img-src 'self' data:; font-src 'self' data:; connect-src 'self' https:; " + "base-uri 'self'; form-action 'self'; object-src 'none'; frame-ancestors 'none'"; + /// + /// Validates. + /// public void Validate() { if (string.IsNullOrWhiteSpace(ContentSecurityPolicy)) diff --git a/Proxytrace.Api/Program.cs b/Proxytrace.Api/Program.cs index 6536a51ea..dba1d3353 100644 --- a/Proxytrace.Api/Program.cs +++ b/Proxytrace.Api/Program.cs @@ -172,10 +172,16 @@ namespace Proxytrace.Api /// internal sealed class TrustedProxyConfiguration { + /// + /// The section name constant value. + /// public const string SectionName = "ForwardedHeaders"; private readonly IConfiguration configuration; + /// + /// Initializes a new instance of the class. + /// public TrustedProxyConfiguration(IConfiguration configuration) { this.configuration = configuration; @@ -248,6 +254,9 @@ private System.Net.IPNetwork ParseNetwork(string value) => /// internal sealed class AuthRateLimiterConfigurator { + /// + /// The section name constant value. + /// public const string SectionName = "RateLimiting"; /// @@ -256,16 +265,28 @@ internal sealed class AuthRateLimiterConfigurator /// public const string LoginPolicy = "auth-login"; + /// + /// The password reset policy constant value. + /// public const string PasswordResetPolicy = "auth-reset"; + /// + /// The mfa policy constant value. + /// public const string MfaPolicy = "auth-mfa"; private readonly IConfiguration configuration; + /// + /// Initializes a new instance of the class. + /// public AuthRateLimiterConfigurator(IConfiguration configuration) { this.configuration = configuration; } + /// + /// Configures the application request pipeline. + /// public void Configure(RateLimiterOptions options) { options.RejectionStatusCode = StatusCodes.Status429TooManyRequests; diff --git a/Proxytrace.Api/TestOnlyEndpointAttribute.cs b/Proxytrace.Api/TestOnlyEndpointAttribute.cs index 4089a74e9..61838433e 100644 --- a/Proxytrace.Api/TestOnlyEndpointAttribute.cs +++ b/Proxytrace.Api/TestOnlyEndpointAttribute.cs @@ -12,8 +12,14 @@ namespace Proxytrace.Api; [AttributeUsage(AttributeTargets.Class | AttributeTargets.Method)] public sealed class TestOnlyEndpointAttribute : Attribute, IFilterFactory { + /// + /// Gets the is reusable. + /// public bool IsReusable => true; + /// + /// Creates the instance. + /// public IFilterMetadata CreateInstance(IServiceProvider serviceProvider) => new Filter( serviceProvider.GetRequiredService(), @@ -24,12 +30,18 @@ private sealed class Filter : IActionFilter private readonly IHostEnvironment environment; private readonly IConfiguration configuration; + /// + /// Initializes a new instance of the class. + /// public Filter(IHostEnvironment environment, IConfiguration configuration) { this.environment = environment; this.configuration = configuration; } + /// + /// On action executing. + /// public void OnActionExecuting(ActionExecutingContext context) { bool enabled = environment.IsDevelopment() @@ -40,6 +52,9 @@ public void OnActionExecuting(ActionExecutingContext context) } } + /// + /// On action executed. + /// public void OnActionExecuted(ActionExecutedContext context) { } diff --git a/Proxytrace.Application/Agent/AgentNameGenerator.cs b/Proxytrace.Application/Agent/AgentNameGenerator.cs index 900975c5b..9944905e6 100644 --- a/Proxytrace.Application/Agent/AgentNameGenerator.cs +++ b/Proxytrace.Application/Agent/AgentNameGenerator.cs @@ -18,6 +18,9 @@ internal sealed class AgentNameGenerator : IAgentNameGenerator private readonly IAgentRepository agentRepository; private readonly ILogger logger; + /// + /// Initializes a new instance of the class. + /// public AgentNameGenerator( IPromptTemplateRepository prompts, IAgentRepository agentRepository, @@ -28,6 +31,9 @@ public AgentNameGenerator( this.logger = logger; } + /// + /// Generates the name asynchronously. + /// public async Task GenerateNameAsync( IPromptTemplate promptTemplate, IProject project, diff --git a/Proxytrace.Application/Anomaly/Internal/AnomalyDetectionService.cs b/Proxytrace.Application/Anomaly/Internal/AnomalyDetectionService.cs index 2ed74fc59..c5702f3b8 100644 --- a/Proxytrace.Application/Anomaly/Internal/AnomalyDetectionService.cs +++ b/Proxytrace.Application/Anomaly/Internal/AnomalyDetectionService.cs @@ -28,6 +28,9 @@ internal sealed class AnomalyDetectionService : BackgroundService, IAnomalyDetec SingleWriter = false, }); + /// + /// Initializes a new instance of the class. + /// public AnomalyDetectionService( IAnomalyDetector detector, IAnomalyInputFactory inputFactory, @@ -42,6 +45,9 @@ public AnomalyDetectionService( this.logger = logger; } + /// + /// Enqueues asynchronously. + /// public Task EnqueueAsync(ITestRunGroup testRunGroup, CancellationToken cancellationToken = default) => channel.Writer.WriteAsync(testRunGroup.Id, cancellationToken).AsTask(); diff --git a/Proxytrace.Application/Anomaly/Internal/AnomalyDetector.cs b/Proxytrace.Application/Anomaly/Internal/AnomalyDetector.cs index a490a5d6d..9640652d5 100644 --- a/Proxytrace.Application/Anomaly/Internal/AnomalyDetector.cs +++ b/Proxytrace.Application/Anomaly/Internal/AnomalyDetector.cs @@ -7,11 +7,17 @@ internal sealed class AnomalyDetector : IAnomalyDetector { private readonly AnomalyDetectionConfiguration configuration; + /// + /// Initializes a new instance of the class. + /// public AnomalyDetector(AnomalyDetectionConfiguration configuration) { this.configuration = configuration; } + /// + /// Detects. + /// public IReadOnlyList Detect(AnomalyInput input) { // Rule 1 (hard): the group failed, a run failed, or a run produced no results at all — the diff --git a/Proxytrace.Application/Anomaly/Internal/AnomalyInputFactory.cs b/Proxytrace.Application/Anomaly/Internal/AnomalyInputFactory.cs index 2c9e5d913..daea2199a 100644 --- a/Proxytrace.Application/Anomaly/Internal/AnomalyInputFactory.cs +++ b/Proxytrace.Application/Anomaly/Internal/AnomalyInputFactory.cs @@ -12,6 +12,9 @@ internal sealed class AnomalyInputFactory : IAnomalyInputFactory private readonly IStatsReader runStats; private readonly AnomalyDetectionConfiguration configuration; + /// + /// Initializes a new instance of the class. + /// public AnomalyInputFactory( ITestRunRepository testRuns, IStatsReader runStats, @@ -22,6 +25,9 @@ public AnomalyInputFactory( this.configuration = configuration; } + /// + /// Builds asynchronously. + /// public async Task BuildAsync(ITestRunGroup group, CancellationToken cancellationToken = default) { var runs = await testRuns.GetByGroupAsync(group.Id, cancellationToken); diff --git a/Proxytrace.Application/AuditLog/Internal/AuditChannel.cs b/Proxytrace.Application/AuditLog/Internal/AuditChannel.cs index 9bc2e50fb..7842cac34 100644 --- a/Proxytrace.Application/AuditLog/Internal/AuditChannel.cs +++ b/Proxytrace.Application/AuditLog/Internal/AuditChannel.cs @@ -15,11 +15,20 @@ internal sealed class AuditChannel : IAuditChannel SingleWriter = false, }); + /// + /// Tries to the write. + /// public bool TryWrite(AuditCapture entry) => channel.Writer.TryWrite(entry); + /// + /// Reads the all asynchronously. + /// public IAsyncEnumerable ReadAllAsync(CancellationToken cancellationToken) => channel.Reader.ReadAllAsync(cancellationToken); + /// + /// Tries to the read. + /// public bool TryRead([MaybeNullWhen(false)] out AuditCapture entry) => channel.Reader.TryRead(out entry); } diff --git a/Proxytrace.Application/AuditLog/Internal/AuditChannelLogger.cs b/Proxytrace.Application/AuditLog/Internal/AuditChannelLogger.cs index 2eae54bbb..497439389 100644 --- a/Proxytrace.Application/AuditLog/Internal/AuditChannelLogger.cs +++ b/Proxytrace.Application/AuditLog/Internal/AuditChannelLogger.cs @@ -14,16 +14,28 @@ internal sealed class AuditChannelLogger : ILogger private readonly IAuditChannel channel; private readonly IAuditActorAccessor? actorAccessor; + /// + /// Initializes a new instance of the class. + /// public AuditChannelLogger(IAuditChannel channel, IAuditActorAccessor? actorAccessor) { this.channel = channel; this.actorAccessor = actorAccessor; } + /// + /// Begins the scope. + /// public IDisposable? BeginScope(TState state) where TState : notnull => null; + /// + /// Determines whether the enabled. + /// public bool IsEnabled(LogLevel logLevel) => true; + /// + /// Logs. + /// public void Log( LogLevel logLevel, EventId eventId, diff --git a/Proxytrace.Application/AuditLog/Internal/AuditChannelLoggerProvider.cs b/Proxytrace.Application/AuditLog/Internal/AuditChannelLoggerProvider.cs index 2d6f4275e..9c6088870 100644 --- a/Proxytrace.Application/AuditLog/Internal/AuditChannelLoggerProvider.cs +++ b/Proxytrace.Application/AuditLog/Internal/AuditChannelLoggerProvider.cs @@ -16,14 +16,23 @@ internal sealed class AuditChannelLoggerProvider : ILoggerProvider private readonly AuditChannelLogger logger; + /// + /// Initializes a new instance of the class. + /// public AuditChannelLoggerProvider(IAuditChannel channel, IAuditActorAccessor? actorAccessor) { logger = new AuditChannelLogger(channel, actorAccessor); } + /// + /// Creates the logger. + /// public ILogger CreateLogger(string categoryName) => categoryName == AuditCategory ? logger : NullLogger.Instance; + /// + /// Releases all resources used by the current instance. + /// public void Dispose() { } diff --git a/Proxytrace.Application/AuditLog/Internal/AuditLogCleanupService.cs b/Proxytrace.Application/AuditLog/Internal/AuditLogCleanupService.cs index f539cf97a..47c0321eb 100644 --- a/Proxytrace.Application/AuditLog/Internal/AuditLogCleanupService.cs +++ b/Proxytrace.Application/AuditLog/Internal/AuditLogCleanupService.cs @@ -14,6 +14,9 @@ internal sealed class AuditLogCleanupService : BackgroundService private readonly ILogger logger; private readonly IAuditLogRepository repository; + /// + /// Initializes a new instance of the class. + /// public AuditLogCleanupService( AuditLogCleanupConfiguration configuration, ILogger logger, @@ -29,6 +32,9 @@ public AuditLogCleanupService( } } + /// + /// Cleans the once asynchronously. + /// public async Task CleanOnceAsync(CancellationToken cancellationToken) { try diff --git a/Proxytrace.Application/AuditLog/Internal/AuditWriter.cs b/Proxytrace.Application/AuditLog/Internal/AuditWriter.cs index 1568655eb..9ab0b0925 100644 --- a/Proxytrace.Application/AuditLog/Internal/AuditWriter.cs +++ b/Proxytrace.Application/AuditLog/Internal/AuditWriter.cs @@ -16,6 +16,9 @@ internal sealed class AuditWriter : BackgroundService private readonly IAuditLogEntry.CreateExisting createEntry; private readonly IAuditLogRepository repository; + /// + /// Initializes a new instance of the class. + /// public AuditWriter( IAuditChannel channel, IAuditLogEntry.CreateExisting createEntry, diff --git a/Proxytrace.Application/Auth/AuthMode.cs b/Proxytrace.Application/Auth/AuthMode.cs index 7914a1826..e037a01b8 100644 --- a/Proxytrace.Application/Auth/AuthMode.cs +++ b/Proxytrace.Application/Auth/AuthMode.cs @@ -1,5 +1,8 @@ namespace Proxytrace.Application.Auth; +/// +/// Specifies the auth mode. +/// public enum AuthMode { Oidc, diff --git a/Proxytrace.Application/Auth/AuthOptions.cs b/Proxytrace.Application/Auth/AuthOptions.cs index 2f4096d47..61ee83e79 100644 --- a/Proxytrace.Application/Auth/AuthOptions.cs +++ b/Proxytrace.Application/Auth/AuthOptions.cs @@ -1,8 +1,17 @@ namespace Proxytrace.Application.Auth; +/// +/// Represents a auth options. +/// public sealed class AuthOptions { + /// + /// Gets or sets the oidc. + /// public OidcOptions Oidc { get; init; } = new(); + /// + /// Gets or sets the local. + /// public LocalSection Local { get; init; } = new(); /// @@ -15,22 +24,49 @@ public sealed class AuthOptions /// public bool EmergencyLogResetLink { get; init; } + /// + /// Provides additional functionality. + /// public AuthMode Mode => string.IsNullOrWhiteSpace(Oidc.Authority) ? AuthMode.Local : AuthMode.Oidc; + /// + /// Represents a oidc options. + /// public sealed class OidcOptions { + /// + /// Gets or sets the authority. + /// public string Authority { get; init; } = string.Empty; + /// + /// Gets or sets the audience. + /// public string Audience { get; init; } = string.Empty; + /// + /// Gets or sets the require https metadata. + /// public bool RequireHttpsMetadata { get; init; } = true; + /// + /// Gets or sets the email claim type. + /// public string EmailClaimType { get; init; } = "email"; + /// + /// Gets or sets the name claim type. + /// public string NameClaimType { get; init; } = "name"; } + /// + /// Represents a local section. + /// public sealed class LocalSection { + /// + /// Gets or sets the signing key. + /// public string SigningKey { get; init; } = string.Empty; } } diff --git a/Proxytrace.Application/Auth/IJitUserProvisioner.cs b/Proxytrace.Application/Auth/IJitUserProvisioner.cs index e7f780739..0e27710e5 100644 --- a/Proxytrace.Application/Auth/IJitUserProvisioner.cs +++ b/Proxytrace.Application/Auth/IJitUserProvisioner.cs @@ -2,6 +2,9 @@ namespace Proxytrace.Application.Auth; +/// +/// Represents a jit user provisioner. +/// public interface IJitUserProvisioner { Task EnsureProvisionedAsync( diff --git a/Proxytrace.Application/Auth/IMfaChallengeService.cs b/Proxytrace.Application/Auth/IMfaChallengeService.cs index 0c7c47b7e..2200b2cd8 100644 --- a/Proxytrace.Application/Auth/IMfaChallengeService.cs +++ b/Proxytrace.Application/Auth/IMfaChallengeService.cs @@ -30,4 +30,7 @@ public interface IMfaChallengeService bool RegisterFailure(string token); } +/// +/// Represents a mfa challenge. +/// public sealed record MfaChallenge(string Token, DateTimeOffset ExpiresAt); diff --git a/Proxytrace.Application/Auth/IStreamTicketService.cs b/Proxytrace.Application/Auth/IStreamTicketService.cs index 10c3595a9..0c587941f 100644 --- a/Proxytrace.Application/Auth/IStreamTicketService.cs +++ b/Proxytrace.Application/Auth/IStreamTicketService.cs @@ -20,4 +20,7 @@ public interface IStreamTicketService Guid? Consume(string token); } +/// +/// Represents a stream ticket. +/// public sealed record StreamTicket(string Token, DateTimeOffset ExpiresAt); diff --git a/Proxytrace.Application/Auth/Internal/JitUserProvisioner.cs b/Proxytrace.Application/Auth/Internal/JitUserProvisioner.cs index 8912c8295..5d1870bf3 100644 --- a/Proxytrace.Application/Auth/Internal/JitUserProvisioner.cs +++ b/Proxytrace.Application/Auth/Internal/JitUserProvisioner.cs @@ -12,6 +12,9 @@ internal class JitUserProvisioner : IJitUserProvisioner private readonly ITransaction transaction; private readonly ILogger audit; + /// + /// Initializes a new instance of the class. + /// public JitUserProvisioner( IUserRepository users, IUser.CreateNew createUser, @@ -24,6 +27,9 @@ public JitUserProvisioner( this.audit = audit; } + /// + /// Ensures the provisioned asynchronously. + /// public Task EnsureProvisionedAsync( string externalSubject, string email, diff --git a/Proxytrace.Application/Auth/Internal/MfaChallengeService.cs b/Proxytrace.Application/Auth/Internal/MfaChallengeService.cs index 9b4ee1a18..5d21135a0 100644 --- a/Proxytrace.Application/Auth/Internal/MfaChallengeService.cs +++ b/Proxytrace.Application/Auth/Internal/MfaChallengeService.cs @@ -12,6 +12,9 @@ internal sealed class MfaChallengeService : IMfaChallengeService private readonly ConcurrentDictionary challenges = new(StringComparer.Ordinal); + /// + /// Issue. + /// public MfaChallenge Issue(IUser user) { ArgumentNullException.ThrowIfNull(user); @@ -24,6 +27,9 @@ public MfaChallenge Issue(IUser user) return new MfaChallenge(token, expiresAt); } + /// + /// Peek. + /// public Guid? Peek(string token) { if (string.IsNullOrEmpty(token) || !challenges.TryGetValue(token, out var entry)) @@ -40,6 +46,9 @@ public MfaChallenge Issue(IUser user) return entry.UserId; } + /// + /// Consume. + /// public void Consume(string token) { if (!string.IsNullOrEmpty(token)) @@ -48,6 +57,9 @@ public void Consume(string token) } } + /// + /// Registers the failure. + /// public bool RegisterFailure(string token) { if (string.IsNullOrEmpty(token) || !challenges.TryGetValue(token, out var entry)) @@ -85,8 +97,17 @@ private static string GenerateToken() private sealed class Entry(Guid userId, DateTimeOffset expiresAt) { + /// + /// Gets the user id. + /// public Guid UserId { get; } = userId; + /// + /// Gets the expires at. + /// public DateTimeOffset ExpiresAt { get; } = expiresAt; + /// + /// The failures. + /// public int Failures; } } diff --git a/Proxytrace.Application/Auth/Internal/StreamTicketService.cs b/Proxytrace.Application/Auth/Internal/StreamTicketService.cs index 38515ce5e..5186aa12a 100644 --- a/Proxytrace.Application/Auth/Internal/StreamTicketService.cs +++ b/Proxytrace.Application/Auth/Internal/StreamTicketService.cs @@ -11,6 +11,9 @@ internal sealed class StreamTicketService : IStreamTicketService private readonly ConcurrentDictionary tickets = new(StringComparer.Ordinal); + /// + /// Issue. + /// public StreamTicket Issue(IUser user) { ArgumentNullException.ThrowIfNull(user); @@ -23,6 +26,9 @@ public StreamTicket Issue(IUser user) return new StreamTicket(token, expiresAt); } + /// + /// Consume. + /// public Guid? Consume(string token) { if (string.IsNullOrEmpty(token) || !tickets.TryRemove(token, out var entry)) diff --git a/Proxytrace.Application/Auth/Internal/UserAdministrationService.cs b/Proxytrace.Application/Auth/Internal/UserAdministrationService.cs index 83f1ef9e4..86efc392c 100644 --- a/Proxytrace.Application/Auth/Internal/UserAdministrationService.cs +++ b/Proxytrace.Application/Auth/Internal/UserAdministrationService.cs @@ -8,12 +8,18 @@ internal sealed class UserAdministrationService : IUserAdministrationService private readonly IUserRepository users; private readonly IApiKeyRepository apiKeys; + /// + /// Initializes a new instance of the class. + /// public UserAdministrationService(IUserRepository users, IApiKeyRepository apiKeys) { this.users = users; this.apiKeys = apiKeys; } + /// + /// Change role asynchronously. + /// public async Task ChangeRoleAsync( Guid actingUserId, Guid targetUserId, @@ -37,6 +43,9 @@ public UserAdministrationService(IUserRepository users, IApiKeyRepository apiKey return await target.ChangeRole(newRole, cancellationToken); } + /// + /// Removes asynchronously. + /// public async Task RemoveAsync( Guid actingUserId, Guid targetUserId, diff --git a/Proxytrace.Application/Auth/Local/IInviteService.cs b/Proxytrace.Application/Auth/Local/IInviteService.cs index edbccd22c..adfa41d04 100644 --- a/Proxytrace.Application/Auth/Local/IInviteService.cs +++ b/Proxytrace.Application/Auth/Local/IInviteService.cs @@ -9,6 +9,9 @@ namespace Proxytrace.Application.Auth.Local; /// public sealed record InviteCreated(IInvite Invite, string RawToken); +/// +/// Service that provides invite functionality. +/// public interface IInviteService { Task CreateAsync( diff --git a/Proxytrace.Application/Auth/Local/ILegacyClaimService.cs b/Proxytrace.Application/Auth/Local/ILegacyClaimService.cs index ef9dbb0cd..690b6bf71 100644 --- a/Proxytrace.Application/Auth/Local/ILegacyClaimService.cs +++ b/Proxytrace.Application/Auth/Local/ILegacyClaimService.cs @@ -1,5 +1,8 @@ namespace Proxytrace.Application.Auth.Local; +/// +/// Service that provides legacy claim functionality. +/// public interface ILegacyClaimService { Task IsClaimAvailableAsync(CancellationToken cancellationToken = default); diff --git a/Proxytrace.Application/Auth/Local/ILocalTokenIssuer.cs b/Proxytrace.Application/Auth/Local/ILocalTokenIssuer.cs index 863df7239..c987fb9c5 100644 --- a/Proxytrace.Application/Auth/Local/ILocalTokenIssuer.cs +++ b/Proxytrace.Application/Auth/Local/ILocalTokenIssuer.cs @@ -2,18 +2,42 @@ namespace Proxytrace.Application.Auth.Local; +/// +/// Represents a local token issuer. +/// public interface ILocalTokenIssuer { LocalTokenResult Issue(IUser user); } +/// +/// Encapsulates the result of a local token operation. +/// public sealed record LocalTokenResult(string Token, DateTimeOffset ExpiresAt); +/// +/// Represents a local auth options. +/// public sealed class LocalAuthOptions { + /// + /// The section name constant value. + /// public const string SectionName = "Authentication:Local"; + /// + /// Gets or sets the signing key. + /// public string SigningKey { get; init; } = string.Empty; + /// + /// Gets or sets the issuer. + /// public string Issuer { get; init; } = "proxytrace-local"; + /// + /// Gets or sets the audience. + /// public string Audience { get; init; } = "proxytrace-api"; + /// + /// Gets or sets the token lifetime. + /// public TimeSpan TokenLifetime { get; init; } = TimeSpan.FromDays(7); } diff --git a/Proxytrace.Application/Auth/Local/ILoginService.cs b/Proxytrace.Application/Auth/Local/ILoginService.cs index ff08bcdec..3de6aadb5 100644 --- a/Proxytrace.Application/Auth/Local/ILoginService.cs +++ b/Proxytrace.Application/Auth/Local/ILoginService.cs @@ -2,6 +2,9 @@ namespace Proxytrace.Application.Auth.Local; +/// +/// Service that provides login functionality. +/// public interface ILoginService { /// diff --git a/Proxytrace.Application/Auth/Local/IPasswordPolicy.cs b/Proxytrace.Application/Auth/Local/IPasswordPolicy.cs index fd4a86c21..f936c818c 100644 --- a/Proxytrace.Application/Auth/Local/IPasswordPolicy.cs +++ b/Proxytrace.Application/Auth/Local/IPasswordPolicy.cs @@ -1,14 +1,26 @@ namespace Proxytrace.Application.Auth.Local; +/// +/// Represents a password policy. +/// public interface IPasswordPolicy { PasswordValidationResult Validate(string password); } +/// +/// Encapsulates the result of a password validation operation. +/// public sealed record PasswordValidationResult( bool IsValid, IReadOnlyList Errors) { + /// + /// Ok. + /// public static PasswordValidationResult Ok() => new(true, []); + /// + /// Fail. + /// public static PasswordValidationResult Fail(params string[] errors) => new(false, errors); } diff --git a/Proxytrace.Application/Auth/Local/IPasswordService.cs b/Proxytrace.Application/Auth/Local/IPasswordService.cs index 59ebc4332..a3494404b 100644 --- a/Proxytrace.Application/Auth/Local/IPasswordService.cs +++ b/Proxytrace.Application/Auth/Local/IPasswordService.cs @@ -2,6 +2,9 @@ namespace Proxytrace.Application.Auth.Local; +/// +/// Service that provides password functionality. +/// public interface IPasswordService { string Hash(IUser user, string password); diff --git a/Proxytrace.Application/Auth/Local/Internal/InviteService.cs b/Proxytrace.Application/Auth/Local/Internal/InviteService.cs index 23c347b3c..0537701f1 100644 --- a/Proxytrace.Application/Auth/Local/Internal/InviteService.cs +++ b/Proxytrace.Application/Auth/Local/Internal/InviteService.cs @@ -26,6 +26,9 @@ internal sealed class InviteService : IInviteService private readonly ILicenseService license; private readonly ISecretHasher hasher; + /// + /// Initializes a new instance of the class. + /// public InviteService( IInviteRepository invites, IUserRepository users, @@ -48,6 +51,9 @@ public InviteService( this.hasher = hasher; } + /// + /// Creates asynchronously. + /// public async Task CreateAsync( string email, UserRole role, @@ -68,6 +74,9 @@ public async Task CreateAsync( return new InviteCreated(saved, rawToken); } + /// + /// Gets the by token asynchronously. + /// public async Task GetByTokenAsync(string token, CancellationToken cancellationToken = default) { var invite = await invites.FindByTokenAsync(token, cancellationToken); @@ -84,6 +93,9 @@ public async Task CreateAsync( return invite.IsExpired(DateTimeOffset.UtcNow) ? null : invite; } + /// + /// Consume asynchronously. + /// public Task ConsumeAsync(string token, string password, CancellationToken cancellationToken = default) => transaction.InvokeAsync(async () => { diff --git a/Proxytrace.Application/Auth/Local/Internal/LegacyClaimService.cs b/Proxytrace.Application/Auth/Local/Internal/LegacyClaimService.cs index d4ec2540b..801ce6836 100644 --- a/Proxytrace.Application/Auth/Local/Internal/LegacyClaimService.cs +++ b/Proxytrace.Application/Auth/Local/Internal/LegacyClaimService.cs @@ -10,6 +10,9 @@ internal sealed class LegacyClaimService : ILegacyClaimService private readonly IPasswordService passwords; private readonly ILocalTokenIssuer tokens; + /// + /// Initializes a new instance of the class. + /// public LegacyClaimService( IUserRepository users, IPasswordService passwords, @@ -20,9 +23,15 @@ public LegacyClaimService( this.tokens = tokens; } + /// + /// Determines whether the claim available asynchronously. + /// public async Task IsClaimAvailableAsync(CancellationToken cancellationToken = default) => await FindEligibleAsync(cancellationToken) is not null; + /// + /// Claim asynchronously. + /// public async Task ClaimAsync( string email, string password, diff --git a/Proxytrace.Application/Auth/Local/Internal/LocalTokenIssuer.cs b/Proxytrace.Application/Auth/Local/Internal/LocalTokenIssuer.cs index 8ad0fd7fb..8f4572de3 100644 --- a/Proxytrace.Application/Auth/Local/Internal/LocalTokenIssuer.cs +++ b/Proxytrace.Application/Auth/Local/Internal/LocalTokenIssuer.cs @@ -10,11 +10,17 @@ internal sealed class LocalTokenIssuer : ILocalTokenIssuer { private readonly LocalAuthOptions options; + /// + /// Initializes a new instance of the class. + /// public LocalTokenIssuer(LocalAuthOptions options) { this.options = options; } + /// + /// Issue. + /// public LocalTokenResult Issue(IUser user) { var expires = DateTimeOffset.UtcNow + options.TokenLifetime; diff --git a/Proxytrace.Application/Auth/Local/Internal/LoginService.cs b/Proxytrace.Application/Auth/Local/Internal/LoginService.cs index 0deb63cc6..ef1ebe230 100644 --- a/Proxytrace.Application/Auth/Local/Internal/LoginService.cs +++ b/Proxytrace.Application/Auth/Local/Internal/LoginService.cs @@ -11,6 +11,9 @@ internal sealed class LoginService : ILoginService private readonly IUserTotpEnrollmentRepository enrollments; private readonly IMfaChallengeService challenges; + /// + /// Initializes a new instance of the class. + /// public LoginService( IUserRepository users, IPasswordService passwords, @@ -25,6 +28,9 @@ public LoginService( this.challenges = challenges; } + /// + /// Login asynchronously. + /// public async Task LoginAsync(string email, string password, CancellationToken cancellationToken = default) { var user = await users.FindByEmailAsync(email, cancellationToken); diff --git a/Proxytrace.Application/Auth/Local/Internal/MfaService.cs b/Proxytrace.Application/Auth/Local/Internal/MfaService.cs index 5e8fb09f5..d16502260 100644 --- a/Proxytrace.Application/Auth/Local/Internal/MfaService.cs +++ b/Proxytrace.Application/Auth/Local/Internal/MfaService.cs @@ -34,6 +34,9 @@ internal sealed class MfaService : IMfaService private readonly IPasswordService passwords; private readonly ITransaction transaction; + /// + /// Initializes a new instance of the class. + /// public MfaService( IUserTotpEnrollmentRepository enrollments, IMfaBackupCodeRepository backupCodes, @@ -60,9 +63,15 @@ public MfaService( this.transaction = transaction; } + /// + /// Determines whether the enabled asynchronously. + /// public async Task IsEnabledAsync(Guid userId, CancellationToken cancellationToken = default) => await enrollments.FindByUserAsync(userId, cancellationToken) is { IsConfirmed: true }; + /// + /// Setup asynchronously. + /// public async Task SetupAsync(IUser user, CancellationToken cancellationToken = default) { var existing = await enrollments.FindByUserAsync(user.Id, cancellationToken); @@ -117,6 +126,9 @@ private static bool IsPerUserUniqueViolation(Exception ex) return false; } + /// + /// Activate asynchronously. + /// public async Task?> ActivateAsync(IUser user, string code, CancellationToken cancellationToken = default) { var enrollment = await enrollments.FindByUserAsync(user.Id, cancellationToken); @@ -142,6 +154,9 @@ private static bool IsPerUserUniqueViolation(Exception ex) }, cancellationToken); } + /// + /// Disables asynchronously. + /// public async Task DisableAsync(IUser user, string password, CancellationToken cancellationToken = default) { if (string.IsNullOrEmpty(user.PasswordHash) || !passwords.Verify(user, user.PasswordHash, password)) @@ -152,9 +167,15 @@ private static bool IsPerUserUniqueViolation(Exception ex) return await RemoveEnrollmentAsync(user.Id, cancellationToken); } + /// + /// Admin disable asynchronously. + /// public Task AdminDisableAsync(Guid userId, CancellationToken cancellationToken = default) => RemoveEnrollmentAsync(userId, cancellationToken); + /// + /// Verifies the challenge asynchronously. + /// public async Task VerifyChallengeAsync(string challengeToken, string code, CancellationToken cancellationToken = default) { var userId = challenges.Peek(challengeToken); diff --git a/Proxytrace.Application/Auth/Local/Internal/PasswordPolicy.cs b/Proxytrace.Application/Auth/Local/Internal/PasswordPolicy.cs index c916d20a1..917739edf 100644 --- a/Proxytrace.Application/Auth/Local/Internal/PasswordPolicy.cs +++ b/Proxytrace.Application/Auth/Local/Internal/PasswordPolicy.cs @@ -2,6 +2,9 @@ namespace Proxytrace.Application.Auth.Local.Internal; internal sealed class PasswordPolicy : IPasswordPolicy { + /// + /// Validates. + /// public PasswordValidationResult Validate(string password) { var errors = new List(); diff --git a/Proxytrace.Application/Auth/Local/Internal/PasswordResetService.cs b/Proxytrace.Application/Auth/Local/Internal/PasswordResetService.cs index 3e5f8a297..2c4dc99e0 100644 --- a/Proxytrace.Application/Auth/Local/Internal/PasswordResetService.cs +++ b/Proxytrace.Application/Auth/Local/Internal/PasswordResetService.cs @@ -37,6 +37,9 @@ internal sealed class PasswordResetService : IPasswordResetService private readonly AuthOptions authOptions; private readonly ILogger logger; + /// + /// Initializes a new instance of the class. + /// public PasswordResetService( IPasswordResetTokenRepository tokens, IUserRepository users, @@ -67,6 +70,9 @@ public PasswordResetService( this.logger = logger; } + /// + /// Requests the reset asynchronously. + /// public async Task RequestResetAsync(string email, Func buildResetUrl, CancellationToken cancellationToken = default) { var user = await users.FindByEmailAsync(email, cancellationToken); @@ -132,6 +138,9 @@ private void LogResetLinkFallback(IUser user, IssuedReset issued, int minutes, s user.Email.ToSingleLogLine(), reason, minutes, TokenHint(issued.TokenHash)); } + /// + /// Issue reset link asynchronously. + /// public async Task IssueResetLinkAsync(Guid userId, Func buildResetUrl, CancellationToken cancellationToken = default) { var user = await users.FindAsync(userId, cancellationToken); @@ -143,6 +152,9 @@ private void LogResetLinkFallback(IUser user, IssuedReset issued, int minutes, s return (await IssueAsync(user, buildResetUrl, cancellationToken)).ResetLink; } + /// + /// Completes the reset asynchronously. + /// public Task CompleteResetAsync(string token, string newPassword, CancellationToken cancellationToken = default) => transaction.InvokeAsync(async () => { diff --git a/Proxytrace.Application/Auth/Local/Internal/PasswordService.cs b/Proxytrace.Application/Auth/Local/Internal/PasswordService.cs index 7672ee179..4a0e7a11d 100644 --- a/Proxytrace.Application/Auth/Local/Internal/PasswordService.cs +++ b/Proxytrace.Application/Auth/Local/Internal/PasswordService.cs @@ -22,15 +22,24 @@ internal sealed class PasswordService : IPasswordService Convert.ToHexString(RandomNumberGenerator.GetBytes(32))), LazyThreadSafetyMode.ExecutionAndPublication); + /// + /// Hashes. + /// public string Hash(IUser user, string password) => hasher.HashPassword(user, password); + /// + /// Verifies. + /// public bool Verify(IUser user, string hash, string password) { var result = hasher.VerifyHashedPassword(user, hash, password); return result is PasswordVerificationResult.Success or PasswordVerificationResult.SuccessRehashNeeded; } + /// + /// Verifies the dummy. + /// public void VerifyDummy(string password) { // The result is deliberately unused — only the elapsed work matters. The call is not diff --git a/Proxytrace.Application/Auth/Local/Internal/TotpService.cs b/Proxytrace.Application/Auth/Local/Internal/TotpService.cs index 82266fa0b..b48e4caee 100644 --- a/Proxytrace.Application/Auth/Local/Internal/TotpService.cs +++ b/Proxytrace.Application/Auth/Local/Internal/TotpService.cs @@ -13,9 +13,15 @@ internal sealed class TotpService : ITotpService // (or who types the code as it rolls over) still authenticates. private static readonly VerificationWindow Window = new(previous: 1, future: 1); + /// + /// Generates the secret. + /// public string GenerateSecret() => Base32Encoding.ToString(KeyGeneration.GenerateRandomKey(SecretBytes)); + /// + /// Builds the otp auth uri. + /// public string BuildOtpAuthUri(string email, string secret) { var label = Uri.EscapeDataString($"{Issuer}:{email}"); @@ -23,6 +29,9 @@ public string BuildOtpAuthUri(string email, string secret) return $"otpauth://totp/{label}?secret={secret}&issuer={issuer}&algorithm=SHA1&digits=6&period=30"; } + /// + /// Tries to the verify. + /// public bool TryVerify(string secret, string code, long? lastUsedStep, out long matchedStep) { matchedStep = 0; diff --git a/Proxytrace.Application/Auth/UserAdministrationException.cs b/Proxytrace.Application/Auth/UserAdministrationException.cs index 189163711..c20c34086 100644 --- a/Proxytrace.Application/Auth/UserAdministrationException.cs +++ b/Proxytrace.Application/Auth/UserAdministrationException.cs @@ -7,6 +7,9 @@ namespace Proxytrace.Application.Auth; /// public sealed class UserAdministrationException : Exception { + /// + /// Initializes a new instance of the class. + /// public UserAdministrationException(string message) : base(message) { } diff --git a/Proxytrace.Application/Cleanup/AgentCallCleanupConfiguration.cs b/Proxytrace.Application/Cleanup/AgentCallCleanupConfiguration.cs index c54f47c11..6bfc2045f 100644 --- a/Proxytrace.Application/Cleanup/AgentCallCleanupConfiguration.cs +++ b/Proxytrace.Application/Cleanup/AgentCallCleanupConfiguration.cs @@ -1,7 +1,16 @@ namespace Proxytrace.Application.Cleanup; +/// +/// Configuration for agent call cleanup. +/// public sealed record AgentCallCleanupConfiguration { + /// + /// Gets or sets the retention duration days. + /// public int RetentionDurationDays { get; init; } = 30; + /// + /// Gets or sets the cleanup interval hours. + /// public int CleanupIntervalHours { get; init; } = 6; } diff --git a/Proxytrace.Application/Cleanup/IDataCleanupService.cs b/Proxytrace.Application/Cleanup/IDataCleanupService.cs index e66f4b8f3..725636e74 100644 --- a/Proxytrace.Application/Cleanup/IDataCleanupService.cs +++ b/Proxytrace.Application/Cleanup/IDataCleanupService.cs @@ -1,5 +1,8 @@ namespace Proxytrace.Application.Cleanup; +/// +/// Service that provides data cleanup functionality. +/// public interface IDataCleanupService { Task DeleteAllNonModelDataAsync(CancellationToken cancellationToken = default); diff --git a/Proxytrace.Application/Cleanup/Internal/AgentCallCleanupService.cs b/Proxytrace.Application/Cleanup/Internal/AgentCallCleanupService.cs index 9a4c143d0..49cb92689 100644 --- a/Proxytrace.Application/Cleanup/Internal/AgentCallCleanupService.cs +++ b/Proxytrace.Application/Cleanup/Internal/AgentCallCleanupService.cs @@ -16,6 +16,9 @@ internal sealed class AgentCallCleanupService : BackgroundService private readonly int configuredRetentionDays; + /// + /// Initializes a new instance of the class. + /// public AgentCallCleanupService( AgentCallCleanupConfiguration configuration, ILogger logger, @@ -37,6 +40,9 @@ public AgentCallCleanupService( configuredRetentionDays = configuration.RetentionDurationDays; } + /// + /// Cleans the once asynchronously. + /// public async Task CleanOnceAsync(CancellationToken cancellationToken) { try diff --git a/Proxytrace.Application/Cleanup/Internal/DataCleanupService.cs b/Proxytrace.Application/Cleanup/Internal/DataCleanupService.cs index f910bc5a6..1cbdb6ba1 100644 --- a/Proxytrace.Application/Cleanup/Internal/DataCleanupService.cs +++ b/Proxytrace.Application/Cleanup/Internal/DataCleanupService.cs @@ -16,6 +16,9 @@ internal class DataCleanupService : IDataCleanupService private readonly IRepository agentCalls; private readonly ITransaction transaction; + /// + /// Initializes a new instance of the class. + /// public DataCleanupService( IRepository proposals, IRepository testResults, @@ -32,6 +35,9 @@ public DataCleanupService( this.transaction = transaction; } + /// + /// Deletes the all non model data asynchronously. + /// public Task DeleteAllNonModelDataAsync(CancellationToken cancellationToken = default) => transaction.InvokeAsync(async () => { diff --git a/Proxytrace.Application/CostControl/Internal/CostBudgetGuard.cs b/Proxytrace.Application/CostControl/Internal/CostBudgetGuard.cs index 04d1f0397..a2e7b1278 100644 --- a/Proxytrace.Application/CostControl/Internal/CostBudgetGuard.cs +++ b/Proxytrace.Application/CostControl/Internal/CostBudgetGuard.cs @@ -37,6 +37,9 @@ internal sealed class CostBudgetGuard : BackgroundService private readonly ILogger auditLogger; private readonly ILogger logger; + /// + /// Initializes a new instance of the class. + /// public CostBudgetGuard( ICostStatistics costStatistics, ICostLimitRepository costLimits, diff --git a/Proxytrace.Application/CostControl/Internal/CostStatistics.cs b/Proxytrace.Application/CostControl/Internal/CostStatistics.cs index 501f178d2..33d46b672 100644 --- a/Proxytrace.Application/CostControl/Internal/CostStatistics.cs +++ b/Proxytrace.Application/CostControl/Internal/CostStatistics.cs @@ -24,6 +24,9 @@ internal class CostStatistics : ICostStatistics private readonly IApiKeyRepository apiKeys; private readonly IClock clock; + /// + /// Initializes a new instance of the class. + /// public CostStatistics( IAgentCallStatsReader callStats, ICostLimitRepository costLimits, @@ -40,6 +43,9 @@ public CostStatistics( this.clock = clock; } + /// + /// Gets the month to date spend asynchronously. + /// public Task> GetMonthToDateSpendAsync( DateTimeOffset monthStart, CancellationToken cancellationToken = default) @@ -47,6 +53,9 @@ public Task> GetMonthToDateSpendAsync( new StatisticsFilter(From: monthStart), cancellationToken); + /// + /// Gets the month to date spend by api key asynchronously. + /// public Task> GetMonthToDateSpendByApiKeyAsync( DateTimeOffset monthStart, CancellationToken cancellationToken = default) @@ -54,6 +63,9 @@ public Task> GetMonthToDateSpendByApiKeyAsy new StatisticsFilter(From: monthStart), cancellationToken); + /// + /// Gets the cost overview asynchronously. + /// public async Task GetCostOverviewAsync( Guid projectId, DateTimeOffset from, @@ -157,6 +169,9 @@ await Task.WhenAll( Bucket: effectiveBucket); } + /// + /// Gets the budget status asynchronously. + /// public async Task> GetBudgetStatusAsync( Guid projectId, CancellationToken cancellationToken = default) diff --git a/Proxytrace.Application/CustomAnomaly/Internal/BlockedCallRecorder.cs b/Proxytrace.Application/CustomAnomaly/Internal/BlockedCallRecorder.cs index 62e1ca48f..a2de94f83 100644 --- a/Proxytrace.Application/CustomAnomaly/Internal/BlockedCallRecorder.cs +++ b/Proxytrace.Application/CustomAnomaly/Internal/BlockedCallRecorder.cs @@ -31,6 +31,9 @@ internal sealed class BlockedCallRecorder : IBlockedCallRecorder private readonly INotificationService notifications; private readonly ILogger logger; + /// + /// Initializes a new instance of the class. + /// public BlockedCallRecorder( ICustomAnomalyResultRepository results, ICustomAnomalyResult.CreateNew createResult, @@ -45,6 +48,9 @@ public BlockedCallRecorder( this.logger = logger; } + /// + /// Record asynchronously. + /// public async Task RecordAsync( IAgentCall call, Guid detectorId, diff --git a/Proxytrace.Application/CustomAnomaly/Internal/CustomAnomalyReviewService.cs b/Proxytrace.Application/CustomAnomaly/Internal/CustomAnomalyReviewService.cs index bf805d73d..44469fb22 100644 --- a/Proxytrace.Application/CustomAnomaly/Internal/CustomAnomalyReviewService.cs +++ b/Proxytrace.Application/CustomAnomaly/Internal/CustomAnomalyReviewService.cs @@ -43,6 +43,9 @@ internal sealed class CustomAnomalyReviewService : BackgroundService, ICustomAno SingleWriter = false, }); + /// + /// Initializes a new instance of the class. + /// public CustomAnomalyReviewService( ILicenseService license, IAgentCallRepository agentCalls, @@ -63,6 +66,9 @@ public CustomAnomalyReviewService( this.logger = logger; } + /// + /// Enqueues asynchronously. + /// public Task EnqueueAsync(Guid agentCallId, CancellationToken cancellationToken = default) => channel.Writer.WriteAsync(agentCallId, cancellationToken).AsTask(); diff --git a/Proxytrace.Application/Demo/DemoSeedContext.cs b/Proxytrace.Application/Demo/DemoSeedContext.cs index ff588355b..56ad6cf39 100644 --- a/Proxytrace.Application/Demo/DemoSeedContext.cs +++ b/Proxytrace.Application/Demo/DemoSeedContext.cs @@ -14,7 +14,13 @@ namespace Proxytrace.Application.Demo; internal sealed class DemoSeedContext { + /// + /// Gets or sets the demo user. + /// public IUser? DemoUser { get; set; } + /// + /// Gets or sets the project. + /// public IProject? Project { get; set; } /// @@ -24,19 +30,52 @@ internal sealed class DemoSeedContext /// public IModelProvider? KioskLiveProvider { get; set; } + /// + /// Gets or sets the gpt54 endpoint. + /// public IModelEndpoint? Gpt54Endpoint { get; set; } + /// + /// Gets or sets the gpt54 mini endpoint. + /// public IModelEndpoint? Gpt54MiniEndpoint { get; set; } + /// + /// Gets or sets the claude endpoint. + /// public IModelEndpoint? ClaudeEndpoint { get; set; } + /// + /// Gets or sets the customer support agent. + /// public IAgent? CustomerSupportAgent { get; set; } + /// + /// Gets or sets the code review agent. + /// public IAgent? CodeReviewAgent { get; set; } + /// + /// Gets or sets the data analytics agent. + /// public IAgent? DataAnalyticsAgent { get; set; } + /// + /// Gets or sets the email triage agent. + /// public IAgent? EmailTriageAgent { get; set; } + /// + /// Gets or sets the helpfulness. + /// public IAgenticEvaluator? Helpfulness { get; set; } + /// + /// Gets or sets the politeness. + /// public IAgenticEvaluator? Politeness { get; set; } + /// + /// Gets the suites by key. + /// public Dictionary SuitesByKey { get; } = new(); + /// + /// Gets the all runs. + /// public List AllRuns { get; } = []; /// @@ -50,21 +89,66 @@ internal sealed class DemoSeedContext /// real anomaly detector fires on them during seeding. /// public ITestRunGroup? RegressedTriageGroup { get; set; } + /// + /// Gets or sets the failed tone group. + /// public ITestRunGroup? FailedToneGroup { get; set; } + /// + /// Require demo user. + /// public IUser RequireDemoUser() => DemoUser ?? throw Missing(nameof(DemoUser)); + /// + /// Require project. + /// public IProject RequireProject() => Project ?? throw Missing(nameof(Project)); + /// + /// Require kiosk live provider. + /// public IModelProvider RequireKioskLiveProvider() => KioskLiveProvider ?? throw Missing(nameof(KioskLiveProvider)); + /// + /// Require gpt54 endpoint. + /// public IModelEndpoint RequireGpt54Endpoint() => Gpt54Endpoint ?? throw Missing(nameof(Gpt54Endpoint)); + /// + /// Require gpt54 mini endpoint. + /// public IModelEndpoint RequireGpt54MiniEndpoint() => Gpt54MiniEndpoint ?? throw Missing(nameof(Gpt54MiniEndpoint)); + /// + /// Require claude endpoint. + /// public IModelEndpoint RequireClaudeEndpoint() => ClaudeEndpoint ?? throw Missing(nameof(ClaudeEndpoint)); + /// + /// Require customer support agent. + /// public IAgent RequireCustomerSupportAgent() => CustomerSupportAgent ?? throw Missing(nameof(CustomerSupportAgent)); + /// + /// Require code review agent. + /// public IAgent RequireCodeReviewAgent() => CodeReviewAgent ?? throw Missing(nameof(CodeReviewAgent)); + /// + /// Require data analytics agent. + /// public IAgent RequireDataAnalyticsAgent() => DataAnalyticsAgent ?? throw Missing(nameof(DataAnalyticsAgent)); + /// + /// Require email triage agent. + /// public IAgent RequireEmailTriageAgent() => EmailTriageAgent ?? throw Missing(nameof(EmailTriageAgent)); + /// + /// Require regressed triage group. + /// public ITestRunGroup RequireRegressedTriageGroup() => RegressedTriageGroup ?? throw Missing(nameof(RegressedTriageGroup)); + /// + /// Require failed tone group. + /// public ITestRunGroup RequireFailedToneGroup() => FailedToneGroup ?? throw Missing(nameof(FailedToneGroup)); + /// + /// Require helpfulness. + /// public IAgenticEvaluator RequireHelpfulness() => Helpfulness ?? throw Missing(nameof(Helpfulness)); + /// + /// Require politeness. + /// public IAgenticEvaluator RequirePoliteness() => Politeness ?? throw Missing(nameof(Politeness)); private static InvalidOperationException Missing(string name) diff --git a/Proxytrace.Application/Demo/Internal/DemoSeederHostedService.cs b/Proxytrace.Application/Demo/Internal/DemoSeederHostedService.cs index 84903224e..efe440d28 100644 --- a/Proxytrace.Application/Demo/Internal/DemoSeederHostedService.cs +++ b/Proxytrace.Application/Demo/Internal/DemoSeederHostedService.cs @@ -10,6 +10,9 @@ internal sealed class DemoSeederHostedService : IHostedService private readonly IServiceProvider rootServices; private readonly ILogger logger; + /// + /// Initializes a new instance of the class. + /// public DemoSeederHostedService( IServiceProvider rootServices, ILogger logger) @@ -18,6 +21,9 @@ public DemoSeederHostedService( this.logger = logger; } + /// + /// Starts asynchronously. + /// public async Task StartAsync(CancellationToken cancellationToken) { using var scope = rootServices.CreateScope(); @@ -49,5 +55,8 @@ public async Task StartAsync(CancellationToken cancellationToken) logger.LogInformation("Kiosk demo seeding complete"); } + /// + /// Stops asynchronously. + /// public Task StopAsync(CancellationToken cancellationToken) => Task.CompletedTask; } diff --git a/Proxytrace.Application/Demo/Scenarios/AnomalySeedScenario.cs b/Proxytrace.Application/Demo/Scenarios/AnomalySeedScenario.cs index 567d5957b..3f0300e5b 100644 --- a/Proxytrace.Application/Demo/Scenarios/AnomalySeedScenario.cs +++ b/Proxytrace.Application/Demo/Scenarios/AnomalySeedScenario.cs @@ -27,6 +27,9 @@ internal sealed class AnomalySeedScenario : IDemoScenario private readonly IRepository groupRepo; private readonly IReadOnlyList runStatsProjectors; + /// + /// Initializes a new instance of the class. + /// public AnomalySeedScenario( DemoSeedContext ctx, IAnomalyInputFactory inputFactory, @@ -50,8 +53,14 @@ public AnomalySeedScenario( // After the statistics backfill (40) has backdated the run history (baselines need a // time-ordered window) and after the notification scenario (50), so the detector's alerts land // newest in the inbox. + /// + /// Gets the order. + /// public int Order => 60; + /// + /// Seeds asynchronously. + /// public async Task SeedAsync(CancellationToken cancellationToken) { // Live stats projection runs debounced in a background worker, so at this point in the diff --git a/Proxytrace.Application/Demo/Scenarios/CoreSeedScenario.cs b/Proxytrace.Application/Demo/Scenarios/CoreSeedScenario.cs index 9bb595883..e8ab3d855 100644 --- a/Proxytrace.Application/Demo/Scenarios/CoreSeedScenario.cs +++ b/Proxytrace.Application/Demo/Scenarios/CoreSeedScenario.cs @@ -38,6 +38,9 @@ internal sealed class CoreSeedScenario : IDemoScenario private readonly IAgentCall.CreateNew agentCallFactory; private readonly ICompletion.Create completionFactory; + /// + /// Initializes a new instance of the class. + /// public CoreSeedScenario( KioskOptions kiosk, KioskEndpointOptions kioskEndpoint, @@ -76,8 +79,14 @@ public CoreSeedScenario( this.completionFactory = completionFactory; } + /// + /// Gets the order. + /// public int Order => 0; + /// + /// Seeds asynchronously. + /// public async Task SeedAsync(CancellationToken cancellationToken) { // Admin, not Member: the kiosk runs on a perpetual Enterprise override license so it can diff --git a/Proxytrace.Application/Demo/Scenarios/DemoApiKeySeedScenario.cs b/Proxytrace.Application/Demo/Scenarios/DemoApiKeySeedScenario.cs index 62013a217..a8fbe3ab1 100644 --- a/Proxytrace.Application/Demo/Scenarios/DemoApiKeySeedScenario.cs +++ b/Proxytrace.Application/Demo/Scenarios/DemoApiKeySeedScenario.cs @@ -28,6 +28,9 @@ internal sealed class DemoApiKeySeedScenario : IDemoScenario private readonly IApiKeyRepository apiKeys; private readonly ILogger logger; + /// + /// Initializes a new instance of the class. + /// public DemoApiKeySeedScenario( KioskOptions kiosk, KioskEndpointOptions kioskEndpoint, @@ -45,8 +48,14 @@ public DemoApiKeySeedScenario( } // After CoreSeedScenario (0), which creates the project, the demo user and the live provider. + /// + /// Gets the order. + /// public int Order => 5; + /// + /// Seeds asynchronously. + /// public async Task SeedAsync(CancellationToken cancellationToken) { if (!kioskEndpoint.IsConfigured) diff --git a/Proxytrace.Application/Demo/Scenarios/EvaluatorSeedScenario.cs b/Proxytrace.Application/Demo/Scenarios/EvaluatorSeedScenario.cs index ce7450754..a2f556f20 100644 --- a/Proxytrace.Application/Demo/Scenarios/EvaluatorSeedScenario.cs +++ b/Proxytrace.Application/Demo/Scenarios/EvaluatorSeedScenario.cs @@ -18,6 +18,9 @@ internal sealed class EvaluatorSeedScenario : IDemoScenario private readonly IAgenticEvaluator.CreateNew createAgentic; private readonly IRepository evaluatorRepo; + /// + /// Initializes a new instance of the class. + /// public EvaluatorSeedScenario( DemoSeedContext ctx, IAgenticEvaluatorPresets presets, @@ -36,8 +39,14 @@ public EvaluatorSeedScenario( this.evaluatorRepo = evaluatorRepo; } + /// + /// Gets the order. + /// public int Order => 10; + /// + /// Seeds asynchronously. + /// public async Task SeedAsync(CancellationToken cancellationToken) { var allPresets = presets.GetAll(); diff --git a/Proxytrace.Application/Demo/Scenarios/NotificationSeedScenario.cs b/Proxytrace.Application/Demo/Scenarios/NotificationSeedScenario.cs index 51ce82ec0..937c3bdb7 100644 --- a/Proxytrace.Application/Demo/Scenarios/NotificationSeedScenario.cs +++ b/Proxytrace.Application/Demo/Scenarios/NotificationSeedScenario.cs @@ -17,6 +17,9 @@ internal sealed class NotificationSeedScenario : IDemoScenario private readonly INotificationRepository notifications; private readonly IRepository proposals; + /// + /// Initializes a new instance of the class. + /// public NotificationSeedScenario( DemoSeedContext ctx, INotification.CreateNew createNotification, @@ -30,6 +33,9 @@ public NotificationSeedScenario( } // After OptimizationProposalSeedScenario (40) so a proposal is available to point at. + /// + /// Gets the order. + /// public int Order => 50; private sealed record NotificationSpec( @@ -41,6 +47,9 @@ private sealed record NotificationSpec( Guid? TargetId, bool Read); + /// + /// Seeds asynchronously. + /// public async Task SeedAsync(CancellationToken cancellationToken) { var projectId = ctx.RequireProject().Id; diff --git a/Proxytrace.Application/Demo/Scenarios/OptimizationProposalSeedScenario.cs b/Proxytrace.Application/Demo/Scenarios/OptimizationProposalSeedScenario.cs index 53169d2b0..d36fd2710 100644 --- a/Proxytrace.Application/Demo/Scenarios/OptimizationProposalSeedScenario.cs +++ b/Proxytrace.Application/Demo/Scenarios/OptimizationProposalSeedScenario.cs @@ -15,6 +15,9 @@ internal sealed class OptimizationProposalSeedScenario : IDemoScenario private readonly IToolUpdateProposal.CreateNew createToolUpdate; private readonly IRepository repo; + /// + /// Initializes a new instance of the class. + /// public OptimizationProposalSeedScenario( DemoSeedContext ctx, IModelSwitchProposal.CreateNew createModelSwitch, @@ -29,6 +32,9 @@ public OptimizationProposalSeedScenario( this.repo = repo; } + /// + /// Gets the order. + /// public int Order => 40; private sealed record ProposalSpec( @@ -36,6 +42,9 @@ private sealed record ProposalSpec( ProposalStatus Status, Func, ITestRun, IOptimizationProposal> BuildDraft); + /// + /// Seeds asynchronously. + /// public async Task SeedAsync(CancellationToken cancellationToken) { var specs = new ProposalSpec[] diff --git a/Proxytrace.Application/Demo/Scenarios/OptimizationTheorySeedScenario.cs b/Proxytrace.Application/Demo/Scenarios/OptimizationTheorySeedScenario.cs index 9672a37d5..fc346f215 100644 --- a/Proxytrace.Application/Demo/Scenarios/OptimizationTheorySeedScenario.cs +++ b/Proxytrace.Application/Demo/Scenarios/OptimizationTheorySeedScenario.cs @@ -34,6 +34,9 @@ internal sealed class OptimizationTheorySeedScenario : IDemoScenario // promoting/dismissing one silently mutates the other and the second "Promote" 409s. private readonly HashSet claimedProposalIds = []; + /// + /// Initializes a new instance of the class. + /// public OptimizationTheorySeedScenario( DemoSeedContext ctx, ISystemPromptTheory.CreateNew createSystemPrompt, @@ -51,8 +54,14 @@ public OptimizationTheorySeedScenario( } // After OptimizationProposalSeedScenario (40) so validated theories can reference a proposal. + /// + /// Gets the order. + /// public int Order => 45; + /// + /// Seeds asynchronously. + /// public async Task SeedAsync(CancellationToken cancellationToken) { var support = ctx.RequireCustomerSupportAgent(); diff --git a/Proxytrace.Application/Demo/Scenarios/OutlierTraceSeedScenario.cs b/Proxytrace.Application/Demo/Scenarios/OutlierTraceSeedScenario.cs index d4767e41a..4a81e3576 100644 --- a/Proxytrace.Application/Demo/Scenarios/OutlierTraceSeedScenario.cs +++ b/Proxytrace.Application/Demo/Scenarios/OutlierTraceSeedScenario.cs @@ -27,6 +27,9 @@ internal sealed class OutlierTraceSeedScenario : IDemoScenario private readonly IModelParameters.Create paramsFactory; private readonly IRepository agentCallRepo; + /// + /// Initializes a new instance of the class. + /// public OutlierTraceSeedScenario( DemoSeedContext ctx, IAgentCall.CreateExisting agentCallExisting, @@ -43,8 +46,14 @@ public OutlierTraceSeedScenario( // After the statistics backfill (40): these sit on top of the dense history as the most // recent, most visible traces. + /// + /// Gets the order. + /// public int Order => 41; + /// + /// Seeds asynchronously. + /// public async Task SeedAsync(CancellationToken cancellationToken) { var now = DateTimeOffset.UtcNow; diff --git a/Proxytrace.Application/Demo/Scenarios/StatisticsBackfillScenario.cs b/Proxytrace.Application/Demo/Scenarios/StatisticsBackfillScenario.cs index 3e0e69a95..57a8cc859 100644 --- a/Proxytrace.Application/Demo/Scenarios/StatisticsBackfillScenario.cs +++ b/Proxytrace.Application/Demo/Scenarios/StatisticsBackfillScenario.cs @@ -228,6 +228,9 @@ private static readonly (string User, string Assistant)[] TriagePool = private readonly IRepository groupRepo; private readonly IRandom random; + /// + /// Initializes a new instance of the class. + /// public StatisticsBackfillScenario( DemoSeedContext ctx, IAgentCall.CreateExisting agentCallExisting, @@ -252,8 +255,14 @@ public StatisticsBackfillScenario( this.random = random; } + /// + /// Gets the order. + /// public int Order => 40; + /// + /// Seeds asynchronously. + /// public async Task SeedAsync(CancellationToken cancellationToken) { var now = DateTimeOffset.UtcNow; diff --git a/Proxytrace.Application/Demo/Scenarios/TestRunSeedScenario.cs b/Proxytrace.Application/Demo/Scenarios/TestRunSeedScenario.cs index b6ec1435f..77e80481d 100644 --- a/Proxytrace.Application/Demo/Scenarios/TestRunSeedScenario.cs +++ b/Proxytrace.Application/Demo/Scenarios/TestRunSeedScenario.cs @@ -21,6 +21,9 @@ internal sealed class TestRunSeedScenario : IDemoScenario private readonly ITestResult.CreateExisting resultExisting; private readonly IEvaluation.Create createEvaluation; + /// + /// Initializes a new instance of the class. + /// public TestRunSeedScenario( DemoSeedContext ctx, ITestRunGroup.CreateExisting groupExisting, @@ -35,6 +38,9 @@ public TestRunSeedScenario( this.createEvaluation = createEvaluation; } + /// + /// Gets the order. + /// public int Order => 30; private sealed record RunSpec( @@ -49,6 +55,9 @@ private sealed record EndpointPick( double PassRate, int LatencyBaseMs = 720); + /// + /// Seeds asynchronously. + /// public async Task SeedAsync(CancellationToken cancellationToken) { var specs = new RunSpec[] diff --git a/Proxytrace.Application/Demo/Scenarios/TestSuiteSeedScenario.cs b/Proxytrace.Application/Demo/Scenarios/TestSuiteSeedScenario.cs index 7b18ca48b..05eb5bfe1 100644 --- a/Proxytrace.Application/Demo/Scenarios/TestSuiteSeedScenario.cs +++ b/Proxytrace.Application/Demo/Scenarios/TestSuiteSeedScenario.cs @@ -15,6 +15,9 @@ internal sealed class TestSuiteSeedScenario : IDemoScenario private readonly IRepository caseRepo; private readonly IRepository suiteRepo; + /// + /// Initializes a new instance of the class. + /// public TestSuiteSeedScenario( DemoSeedContext ctx, ITestCase.CreateNew createTestCase, @@ -29,6 +32,9 @@ public TestSuiteSeedScenario( this.suiteRepo = suiteRepo; } + /// + /// Gets the order. + /// public int Order => 20; private sealed record CaseSpec( @@ -44,6 +50,9 @@ private sealed record SuiteSpec( IReadOnlyCollection Evaluators, IReadOnlyCollection Cases); + /// + /// Seeds asynchronously. + /// public async Task SeedAsync(CancellationToken cancellationToken) { var support = ctx.RequireCustomerSupportAgent(); diff --git a/Proxytrace.Application/ErrorLog/Internal/ErrorLogChannel.cs b/Proxytrace.Application/ErrorLog/Internal/ErrorLogChannel.cs index 57c181500..a260af811 100644 --- a/Proxytrace.Application/ErrorLog/Internal/ErrorLogChannel.cs +++ b/Proxytrace.Application/ErrorLog/Internal/ErrorLogChannel.cs @@ -14,8 +14,14 @@ internal sealed class ErrorLogChannel : IErrorLogChannel SingleWriter = false, }); + /// + /// Tries to the write. + /// public bool TryWrite(ErrorLogEntry entry) => channel.Writer.TryWrite(entry); + /// + /// Reads the all asynchronously. + /// public IAsyncEnumerable ReadAllAsync(CancellationToken cancellationToken) => channel.Reader.ReadAllAsync(cancellationToken); } diff --git a/Proxytrace.Application/ErrorLog/Internal/ErrorLogChannelLogger.cs b/Proxytrace.Application/ErrorLog/Internal/ErrorLogChannelLogger.cs index 154781fd6..285ea4afa 100644 --- a/Proxytrace.Application/ErrorLog/Internal/ErrorLogChannelLogger.cs +++ b/Proxytrace.Application/ErrorLog/Internal/ErrorLogChannelLogger.cs @@ -19,6 +19,9 @@ internal sealed class ErrorLogChannelLogger : ILogger private readonly IErrorLogChannel channel; private readonly Func? scopeAccessor; + /// + /// Initializes a new instance of the class. + /// public ErrorLogChannelLogger( string category, IErrorLogChannel channel, @@ -29,10 +32,19 @@ public ErrorLogChannelLogger( this.scopeAccessor = scopeAccessor; } + /// + /// Begins the scope. + /// public IDisposable? BeginScope(TState state) where TState : notnull => null; + /// + /// Determines whether the enabled. + /// public bool IsEnabled(LogLevel logLevel) => logLevel >= LogLevel.Error; + /// + /// Logs. + /// public void Log( LogLevel logLevel, EventId eventId, diff --git a/Proxytrace.Application/ErrorLog/Internal/ErrorLogChannelLoggerProvider.cs b/Proxytrace.Application/ErrorLog/Internal/ErrorLogChannelLoggerProvider.cs index a39fc8f89..7bf0c03a1 100644 --- a/Proxytrace.Application/ErrorLog/Internal/ErrorLogChannelLoggerProvider.cs +++ b/Proxytrace.Application/ErrorLog/Internal/ErrorLogChannelLoggerProvider.cs @@ -13,18 +13,30 @@ internal sealed class ErrorLogChannelLoggerProvider : ILoggerProvider, ISupportE private readonly IErrorLogChannel channel; private IExternalScopeProvider? scopeProvider; + /// + /// Initializes a new instance of the class. + /// public ErrorLogChannelLoggerProvider(IErrorLogChannel channel) { this.channel = channel; } + /// + /// Sets the scope provider. + /// public void SetScopeProvider(IExternalScopeProvider scopeProvider) => this.scopeProvider = scopeProvider; // The logger reads the scope provider lazily: the logging framework calls SetScopeProvider // after providers are constructed, and loggers are cached, so a snapshot at creation would be null. + /// + /// Creates the logger. + /// public ILogger CreateLogger(string categoryName) => new ErrorLogChannelLogger(categoryName, channel, () => scopeProvider); + /// + /// Releases all resources used by the current instance. + /// public void Dispose() { } diff --git a/Proxytrace.Application/ErrorLog/Internal/ErrorLogCleanupService.cs b/Proxytrace.Application/ErrorLog/Internal/ErrorLogCleanupService.cs index f327f2f3a..56c43053a 100644 --- a/Proxytrace.Application/ErrorLog/Internal/ErrorLogCleanupService.cs +++ b/Proxytrace.Application/ErrorLog/Internal/ErrorLogCleanupService.cs @@ -14,6 +14,9 @@ internal sealed class ErrorLogCleanupService : BackgroundService private readonly ILogger logger; private readonly IApplicationErrorRepository repository; + /// + /// Initializes a new instance of the class. + /// public ErrorLogCleanupService( ErrorLogCleanupConfiguration configuration, ILogger logger, @@ -34,6 +37,9 @@ public ErrorLogCleanupService( } } + /// + /// Cleans the once asynchronously. + /// public async Task CleanOnceAsync(CancellationToken cancellationToken) { try diff --git a/Proxytrace.Application/ErrorLog/Internal/ErrorLogWriter.cs b/Proxytrace.Application/ErrorLog/Internal/ErrorLogWriter.cs index 283d1dc14..ea957faf9 100644 --- a/Proxytrace.Application/ErrorLog/Internal/ErrorLogWriter.cs +++ b/Proxytrace.Application/ErrorLog/Internal/ErrorLogWriter.cs @@ -17,6 +17,9 @@ internal sealed class ErrorLogWriter : BackgroundService private readonly IApplicationError.CreateExisting createErrorWithId; private readonly IApplicationErrorRepository repository; + /// + /// Initializes a new instance of the class. + /// public ErrorLogWriter( IErrorLogChannel channel, IApplicationError.CreateNew createError, diff --git a/Proxytrace.Application/Evaluator/IAgenticEvaluatorPresets.cs b/Proxytrace.Application/Evaluator/IAgenticEvaluatorPresets.cs index aa38362b8..e5169efe3 100644 --- a/Proxytrace.Application/Evaluator/IAgenticEvaluatorPresets.cs +++ b/Proxytrace.Application/Evaluator/IAgenticEvaluatorPresets.cs @@ -1,8 +1,14 @@ namespace Proxytrace.Application.Evaluator; +/// +/// Represents a agentic evaluator presets. +/// public interface IAgenticEvaluatorPresets { IReadOnlyList GetAll(); } +/// +/// Represents a agentic evaluator preset. +/// public sealed record AgenticEvaluatorPreset(string Key, string Name, string SystemPrompt); diff --git a/Proxytrace.Application/Evaluator/Internal/AgenticEvaluatorPresets.cs b/Proxytrace.Application/Evaluator/Internal/AgenticEvaluatorPresets.cs index 33ba8618a..77f585dc3 100644 --- a/Proxytrace.Application/Evaluator/Internal/AgenticEvaluatorPresets.cs +++ b/Proxytrace.Application/Evaluator/Internal/AgenticEvaluatorPresets.cs @@ -10,5 +10,8 @@ internal sealed class AgenticEvaluatorPresets : IAgenticEvaluatorPresets new("tool_usage", "Tool Usage", Prompts.tool_usage_evaluator), ]; + /// + /// Gets the all. + /// public IReadOnlyList GetAll() => Presets; } diff --git a/Proxytrace.Application/Evaluator/Internal/DefaultEvaluatorProvisioner.cs b/Proxytrace.Application/Evaluator/Internal/DefaultEvaluatorProvisioner.cs index 02392ba95..612cb80c6 100644 --- a/Proxytrace.Application/Evaluator/Internal/DefaultEvaluatorProvisioner.cs +++ b/Proxytrace.Application/Evaluator/Internal/DefaultEvaluatorProvisioner.cs @@ -18,6 +18,9 @@ internal sealed class DefaultEvaluatorProvisioner : IDefaultEvaluatorProvisioner private readonly IPromptTemplate.Create createPrompt; private readonly IModelParameters.Create createParameters; + /// + /// Initializes a new instance of the class. + /// public DefaultEvaluatorProvisioner( IAgenticEvaluatorPresets presets, IAgentRepository agents, @@ -38,6 +41,9 @@ public DefaultEvaluatorProvisioner( this.createParameters = createParameters; } + /// + /// Ensures the default evaluators asynchronously. + /// public async Task EnsureDefaultEvaluatorsAsync(IProject project, CancellationToken cancellationToken = default) { ArgumentNullException.ThrowIfNull(project); diff --git a/Proxytrace.Application/Evaluator/Internal/DefaultEvaluatorSeederHostedService.cs b/Proxytrace.Application/Evaluator/Internal/DefaultEvaluatorSeederHostedService.cs index 6423bd680..6b1c95c88 100644 --- a/Proxytrace.Application/Evaluator/Internal/DefaultEvaluatorSeederHostedService.cs +++ b/Proxytrace.Application/Evaluator/Internal/DefaultEvaluatorSeederHostedService.cs @@ -18,6 +18,9 @@ internal sealed class DefaultEvaluatorSeederHostedService : IHostedService private readonly IServiceProvider rootServices; private readonly ILogger logger; + /// + /// Initializes a new instance of the class. + /// public DefaultEvaluatorSeederHostedService( IServiceProvider rootServices, ILogger logger) @@ -26,6 +29,9 @@ public DefaultEvaluatorSeederHostedService( this.logger = logger; } + /// + /// Starts asynchronously. + /// public async Task StartAsync(CancellationToken cancellationToken) { using var scope = rootServices.CreateScope(); @@ -47,5 +53,8 @@ public async Task StartAsync(CancellationToken cancellationToken) logger.LogInformation("Default evaluator seeding complete"); } + /// + /// Stops asynchronously. + /// public Task StopAsync(CancellationToken cancellationToken) => Task.CompletedTask; } diff --git a/Proxytrace.Application/Ingestion/Internal/AgentCallIngestionWorker.cs b/Proxytrace.Application/Ingestion/Internal/AgentCallIngestionWorker.cs index 0c45bfaf1..c33f151f7 100644 --- a/Proxytrace.Application/Ingestion/Internal/AgentCallIngestionWorker.cs +++ b/Proxytrace.Application/Ingestion/Internal/AgentCallIngestionWorker.cs @@ -53,6 +53,9 @@ internal sealed class AgentCallIngestionWorker : BackgroundService // single in-process consumer is blocked on the one envelope while it retries. private static readonly TimeSpan InlineRetryBackoff = TimeSpan.FromMilliseconds(200); + /// + /// Initializes a new instance of the class. + /// public AgentCallIngestionWorker( IIngestionStream stream, IIngestionExecutor executor, diff --git a/Proxytrace.Application/Ingestion/Internal/AgentCallProcessor.cs b/Proxytrace.Application/Ingestion/Internal/AgentCallProcessor.cs index 407306a92..d5869e091 100644 --- a/Proxytrace.Application/Ingestion/Internal/AgentCallProcessor.cs +++ b/Proxytrace.Application/Ingestion/Internal/AgentCallProcessor.cs @@ -35,6 +35,9 @@ internal sealed class AgentCallProcessor : IAgentCallProcessor private readonly ISessionRepository sessionRepository; private readonly ILogger logger; + /// + /// Initializes a new instance of the class. + /// public AgentCallProcessor( IAgentCallRepository agentCallRepository, IAgentCall.CreateNew createNewCall, @@ -67,6 +70,9 @@ public AgentCallProcessor( this.logger = logger; } + /// + /// Ingest asynchronously. + /// public async Task IngestAsync( IngestJob job, CancellationToken cancellationToken) diff --git a/Proxytrace.Application/Ingestion/Internal/AgentVersionMatcher.cs b/Proxytrace.Application/Ingestion/Internal/AgentVersionMatcher.cs index 25ddb8943..751fcb8b0 100644 --- a/Proxytrace.Application/Ingestion/Internal/AgentVersionMatcher.cs +++ b/Proxytrace.Application/Ingestion/Internal/AgentVersionMatcher.cs @@ -8,6 +8,9 @@ namespace Proxytrace.Application.Ingestion.Internal; +/// +/// Represents a agent version matcher. +/// public interface IAgentVersionMatcher { /// @@ -29,6 +32,9 @@ internal sealed class AgentVersionMatcher : IAgentVersionMatcher private readonly IAgentCallRepository calls; private readonly AgentVersioningOptions options; + /// + /// Initializes a new instance of the class. + /// public AgentVersionMatcher( IAgentVersionRepository versions, IAgentCallRepository calls, @@ -39,6 +45,9 @@ public AgentVersionMatcher( this.options = options; } + /// + /// Finds the similar version asynchronously. + /// public async Task FindSimilarVersionAsync( IProject project, IPromptTemplate systemPrompt, diff --git a/Proxytrace.Application/Ingestion/Internal/IngestionExecutor.cs b/Proxytrace.Application/Ingestion/Internal/IngestionExecutor.cs index 9f1bd70c8..f1914fb5a 100644 --- a/Proxytrace.Application/Ingestion/Internal/IngestionExecutor.cs +++ b/Proxytrace.Application/Ingestion/Internal/IngestionExecutor.cs @@ -20,6 +20,9 @@ internal sealed class IngestionExecutor : IIngestionExecutor private readonly ITraceQuotaGuard quotaGuard; private readonly ILogger logger; + /// + /// Initializes a new instance of the class. + /// public IngestionExecutor( IAgentCallProcessor processor, IRepository providerRepository, @@ -34,6 +37,9 @@ public IngestionExecutor( this.logger = logger; } + /// + /// Ingest asynchronously. + /// public async Task IngestAsync(IngestMessage message, CancellationToken cancellationToken = default) { // Once the licensed monthly trace quota is reached, drop further captures rather than diff --git a/Proxytrace.Application/Ingestion/Internal/OpenAiCallParser.cs b/Proxytrace.Application/Ingestion/Internal/OpenAiCallParser.cs index 1cf4b13a9..c7dc89eff 100644 --- a/Proxytrace.Application/Ingestion/Internal/OpenAiCallParser.cs +++ b/Proxytrace.Application/Ingestion/Internal/OpenAiCallParser.cs @@ -15,6 +15,9 @@ internal class OpenAiCallParser : IOpenAiCallParser private readonly IModelEndpointRepository endpointRepository; private readonly IModelParameters.Create modelParametersFactory; + /// + /// Initializes a new instance of the class. + /// public OpenAiCallParser( ICompletion.Create completionFactory, IModelEndpointRepository endpointRepository, @@ -25,6 +28,9 @@ public OpenAiCallParser( this.modelParametersFactory = modelParametersFactory; } + /// + /// Tries to the parse. + /// public async Task TryParse(IModelProvider provider, string requestBody, string? responseBody, @@ -512,8 +518,17 @@ private static void AccumulateToolCallDeltas( /// private sealed class StreamedToolCall { + /// + /// Gets or sets the id. + /// public string Id { get; set; } = ""; + /// + /// Gets or sets the name. + /// public string Name { get; set; } = ""; + /// + /// Gets the arguments. + /// public StringBuilder Arguments { get; } = new(); } diff --git a/Proxytrace.Application/Ingestion/Internal/TraceQuotaGuard.cs b/Proxytrace.Application/Ingestion/Internal/TraceQuotaGuard.cs index 2622f6b16..2b828d147 100644 --- a/Proxytrace.Application/Ingestion/Internal/TraceQuotaGuard.cs +++ b/Proxytrace.Application/Ingestion/Internal/TraceQuotaGuard.cs @@ -59,6 +59,9 @@ internal sealed class TraceQuotaGuard : BackgroundService, ITraceQuotaGuard private readonly ConcurrentDictionary reportedProjects = new(); private string reportedMonth = string.Empty; + /// + /// Initializes a new instance of the class. + /// public TraceQuotaGuard( IAgentCallRepository agentCalls, IProjectRepository projects, @@ -75,8 +78,14 @@ public TraceQuotaGuard( this.logger = logger; } + /// + /// Gets the is current month over quota. + /// public bool IsCurrentMonthOverQuota => overQuota; + /// + /// Determines whether the over quota. + /// public bool IsOverQuota(Guid projectId) { if (!overQuota) diff --git a/Proxytrace.Application/Licensing/Internal/LicenseKeyManager.cs b/Proxytrace.Application/Licensing/Internal/LicenseKeyManager.cs index 890e194d7..10a87480a 100644 --- a/Proxytrace.Application/Licensing/Internal/LicenseKeyManager.cs +++ b/Proxytrace.Application/Licensing/Internal/LicenseKeyManager.cs @@ -9,6 +9,9 @@ internal sealed class LicenseKeyManager : ILicenseKeyManager private readonly ILicenseActivator activator; private readonly ILicenseService licenseService; + /// + /// Initializes a new instance of the class. + /// public LicenseKeyManager( IStoredLicenseStore store, ILicenseActivator activator, @@ -19,9 +22,15 @@ public LicenseKeyManager( this.licenseService = licenseService; } + /// + /// Validates. + /// public LicenseSnapshot Validate(string licenseJwt) => activator.Validate(licenseJwt); + /// + /// Sets asynchronously. + /// public async Task SetAsync(string licenseJwt, CancellationToken cancellationToken = default) { ArgumentException.ThrowIfNullOrWhiteSpace(licenseJwt); @@ -34,6 +43,9 @@ public async Task SetAsync(string licenseJwt, CancellationToken return activator.Activate(licenseJwt.Trim(), LicenseSource.Stored); } + /// + /// Removes asynchronously. + /// public async Task RemoveAsync(CancellationToken cancellationToken = default) { EnsureManageable(); diff --git a/Proxytrace.Application/Licensing/Internal/StoredLicenseStartupService.cs b/Proxytrace.Application/Licensing/Internal/StoredLicenseStartupService.cs index 698100f00..c87b3f186 100644 --- a/Proxytrace.Application/Licensing/Internal/StoredLicenseStartupService.cs +++ b/Proxytrace.Application/Licensing/Internal/StoredLicenseStartupService.cs @@ -19,6 +19,9 @@ internal sealed class StoredLicenseStartupService : IHostedService private readonly ILicenseService licenseService; private readonly ILogger logger; + /// + /// Initializes a new instance of the class. + /// public StoredLicenseStartupService( IStoredLicenseStore store, ILicenseActivator activator, @@ -31,6 +34,9 @@ public StoredLicenseStartupService( this.logger = logger; } + /// + /// Starts asynchronously. + /// public async Task StartAsync(CancellationToken cancellationToken) { // Kiosk/demo deployments run on a fixed override snapshot; never replace it. @@ -54,6 +60,9 @@ public async Task StartAsync(CancellationToken cancellationToken) } } + /// + /// Stops asynchronously. + /// public Task StopAsync(CancellationToken cancellationToken) => Task.CompletedTask; } diff --git a/Proxytrace.Application/Module.cs b/Proxytrace.Application/Module.cs index 42149b1f9..129b8697b 100644 --- a/Proxytrace.Application/Module.cs +++ b/Proxytrace.Application/Module.cs @@ -53,6 +53,9 @@ namespace Proxytrace.Application; +/// +/// Autofac module that registers services. +/// public sealed class Module : Autofac.Module { protected override void Load(ContainerBuilder builder) diff --git a/Proxytrace.Application/Notifications/Internal/DashboardNotificationChannel.cs b/Proxytrace.Application/Notifications/Internal/DashboardNotificationChannel.cs index 423129fff..0dc0eb49b 100644 --- a/Proxytrace.Application/Notifications/Internal/DashboardNotificationChannel.cs +++ b/Proxytrace.Application/Notifications/Internal/DashboardNotificationChannel.cs @@ -12,13 +12,22 @@ internal sealed class DashboardNotificationChannel : INotificationChannel { private readonly INotificationBroadcaster broadcaster; + /// + /// Initializes a new instance of the class. + /// public DashboardNotificationChannel(INotificationBroadcaster broadcaster) { this.broadcaster = broadcaster; } + /// + /// Gets the name. + /// public string Name => "Dashboard"; + /// + /// Deliver asynchronously. + /// public Task DeliverAsync(INotification notification, CancellationToken cancellationToken = default) { broadcaster.Publish(NotificationCreatedEvent.Create(notification)); diff --git a/Proxytrace.Application/Notifications/Internal/EmailNotificationChannel.cs b/Proxytrace.Application/Notifications/Internal/EmailNotificationChannel.cs index e18332a67..1e50c5d42 100644 --- a/Proxytrace.Application/Notifications/Internal/EmailNotificationChannel.cs +++ b/Proxytrace.Application/Notifications/Internal/EmailNotificationChannel.cs @@ -22,6 +22,9 @@ internal sealed class EmailNotificationChannel : INotificationChannel private readonly IRepository users; private readonly ILogger logger; + /// + /// Initializes a new instance of the class. + /// public EmailNotificationChannel( IEmailSettingsStore settingsStore, IEmailSender sender, @@ -36,8 +39,14 @@ public EmailNotificationChannel( this.logger = logger; } + /// + /// Gets the name. + /// public string Name => "Email"; + /// + /// Deliver asynchronously. + /// public async Task DeliverAsync(INotification notification, CancellationToken cancellationToken = default) { EmailSettings? settings = await settingsStore.GetAsync(cancellationToken); diff --git a/Proxytrace.Application/Notifications/Internal/NotificationService.cs b/Proxytrace.Application/Notifications/Internal/NotificationService.cs index 192687ef9..1607c465d 100644 --- a/Proxytrace.Application/Notifications/Internal/NotificationService.cs +++ b/Proxytrace.Application/Notifications/Internal/NotificationService.cs @@ -10,6 +10,9 @@ internal sealed class NotificationService : INotificationService private readonly INotification.CreateNew createNotification; private readonly ILogger logger; + /// + /// Initializes a new instance of the class. + /// public NotificationService( IEnumerable channels, INotificationRepository notifications, @@ -22,6 +25,9 @@ public NotificationService( this.logger = logger; } + /// + /// Notifies asynchronously. + /// public async Task NotifyAsync(NotificationRequest request, CancellationToken cancellationToken = default) { if (request.TargetKind is { } targetKind && request.TargetId is { } targetId) diff --git a/Proxytrace.Application/Notifications/Internal/SmtpEmailSender.cs b/Proxytrace.Application/Notifications/Internal/SmtpEmailSender.cs index ab017b5bd..c64b940ea 100644 --- a/Proxytrace.Application/Notifications/Internal/SmtpEmailSender.cs +++ b/Proxytrace.Application/Notifications/Internal/SmtpEmailSender.cs @@ -11,11 +11,17 @@ internal sealed class SmtpEmailSender : IEmailSender private readonly IEmailSettingsStore settingsStore; + /// + /// Initializes a new instance of the class. + /// public SmtpEmailSender(IEmailSettingsStore settingsStore) { this.settingsStore = settingsStore; } + /// + /// Sends asynchronously. + /// public async Task SendAsync(EmailMessage message, CancellationToken cancellationToken = default) { EmailSettings? settings = await settingsStore.GetAsync(cancellationToken); diff --git a/Proxytrace.Application/Optimization/IOptimizer.cs b/Proxytrace.Application/Optimization/IOptimizer.cs index 37ee9b3c1..1b8dca70d 100644 --- a/Proxytrace.Application/Optimization/IOptimizer.cs +++ b/Proxytrace.Application/Optimization/IOptimizer.cs @@ -3,6 +3,9 @@ namespace Proxytrace.Application.Optimization; +/// +/// Represents a optimizer. +/// public interface IOptimizer { /// diff --git a/Proxytrace.Application/Optimization/IOptimizerService.cs b/Proxytrace.Application/Optimization/IOptimizerService.cs index 701133c98..2ed74d5fc 100644 --- a/Proxytrace.Application/Optimization/IOptimizerService.cs +++ b/Proxytrace.Application/Optimization/IOptimizerService.cs @@ -2,6 +2,9 @@ namespace Proxytrace.Application.Optimization; +/// +/// Service that provides optimizer functionality. +/// public interface IOptimizerService { /// diff --git a/Proxytrace.Application/Optimization/Internal/Adoption/ProposalAdoptionService.cs b/Proxytrace.Application/Optimization/Internal/Adoption/ProposalAdoptionService.cs index efa0612c9..a2dab5279 100644 --- a/Proxytrace.Application/Optimization/Internal/Adoption/ProposalAdoptionService.cs +++ b/Proxytrace.Application/Optimization/Internal/Adoption/ProposalAdoptionService.cs @@ -39,6 +39,9 @@ internal sealed class ProposalAdoptionService : BackgroundService private readonly ILogger logger; private readonly ILogger audit; + /// + /// Initializes a new instance of the class. + /// public ProposalAdoptionService( IEntityEventService entityEvents, IOptimizationProposalRepository proposals, diff --git a/Proxytrace.Application/Optimization/Internal/CompositeOptimizer.cs b/Proxytrace.Application/Optimization/Internal/CompositeOptimizer.cs index da9fc6c6d..c186d473c 100644 --- a/Proxytrace.Application/Optimization/Internal/CompositeOptimizer.cs +++ b/Proxytrace.Application/Optimization/Internal/CompositeOptimizer.cs @@ -25,6 +25,9 @@ internal sealed class CompositeOptimizer : IOptimizer private readonly IStatsReader runStats; private readonly ILogger logger; + /// + /// Initializes a new instance of the class. + /// public CompositeOptimizer( IReadOnlyCollection optimizers, ITestRunRepository testRuns, @@ -37,6 +40,9 @@ public CompositeOptimizer( this.logger = logger; } + /// + /// Discover theories. + /// public async Task> DiscoverTheories( ITestRunGroup testRunGroup, CancellationToken cancellationToken = default) diff --git a/Proxytrace.Application/Optimization/Internal/Evidence/OptimizerEvidence.cs b/Proxytrace.Application/Optimization/Internal/Evidence/OptimizerEvidence.cs index d49d6aad7..3daa0c81d 100644 --- a/Proxytrace.Application/Optimization/Internal/Evidence/OptimizerEvidence.cs +++ b/Proxytrace.Application/Optimization/Internal/Evidence/OptimizerEvidence.cs @@ -16,6 +16,9 @@ internal sealed record OptimizerEvidence( PropertyNamingPolicy = JsonNamingPolicy.SnakeCaseLower, }; + /// + /// To json. + /// public string ToJson() { var payload = new diff --git a/Proxytrace.Application/Optimization/Internal/Evidence/OptimizerEvidenceBuilder.cs b/Proxytrace.Application/Optimization/Internal/Evidence/OptimizerEvidenceBuilder.cs index e16d11f3e..28888f55d 100644 --- a/Proxytrace.Application/Optimization/Internal/Evidence/OptimizerEvidenceBuilder.cs +++ b/Proxytrace.Application/Optimization/Internal/Evidence/OptimizerEvidenceBuilder.cs @@ -8,6 +8,9 @@ internal class OptimizerEvidenceBuilder : IOptimizerEvidenceBuilder private const int MaxFailing = 20; private const int PassingSampleSize = 3; + /// + /// Builds. + /// public OptimizerEvidence Build(ITestRun run) { var failing = run.TestResults diff --git a/Proxytrace.Application/Optimization/Internal/OptimizationExtensions.cs b/Proxytrace.Application/Optimization/Internal/OptimizationExtensions.cs index 505cf2554..4bafed37b 100644 --- a/Proxytrace.Application/Optimization/Internal/OptimizationExtensions.cs +++ b/Proxytrace.Application/Optimization/Internal/OptimizationExtensions.cs @@ -6,12 +6,21 @@ namespace Proxytrace.Application.Optimization.Internal; internal static class OptimizationExtensions { + /// + /// Gets the optimization priority. + /// public static Priority GetOptimizationPriority(this TestRunStats stats) => GetPriorityFromFailRate(stats.TestCases, stats.Failed); + /// + /// Gets the optimization priority. + /// public static Priority GetOptimizationPriority(this TestRunStatsAggregate stats) => GetPriorityFromFailRate(stats.TestCases, stats.Failed); + /// + /// To aggregate. + /// public static TestRunStatsAggregate ToAggregate(this TestRunStats stats) => new(stats.TestCases, stats.Passed, stats.TotalDuration, stats.Usage, stats.Cost); diff --git a/Proxytrace.Application/Optimization/Internal/OptimizerService.cs b/Proxytrace.Application/Optimization/Internal/OptimizerService.cs index 9067f2a25..eb2c72bb2 100644 --- a/Proxytrace.Application/Optimization/Internal/OptimizerService.cs +++ b/Proxytrace.Application/Optimization/Internal/OptimizerService.cs @@ -28,6 +28,9 @@ internal class OptimizerService : BackgroundService, IOptimizerService SingleWriter = false, }); + /// + /// Initializes a new instance of the class. + /// public OptimizerService( IOptimizer optimizer, ITestRunGroupRepository testRunGroupRepository, @@ -42,6 +45,9 @@ public OptimizerService( this.logger = logger; } + /// + /// Enqueues asynchronously. + /// public Task EnqueueAsync(ITestRunGroup testRunGroup, CancellationToken cancellationToken = default) => channel.Writer.WriteAsync(testRunGroup.Id, cancellationToken).AsTask(); diff --git a/Proxytrace.Application/Optimization/Internal/SwitchModelOptimizer.cs b/Proxytrace.Application/Optimization/Internal/SwitchModelOptimizer.cs index aa55d641f..e7abca4a0 100644 --- a/Proxytrace.Application/Optimization/Internal/SwitchModelOptimizer.cs +++ b/Proxytrace.Application/Optimization/Internal/SwitchModelOptimizer.cs @@ -21,6 +21,9 @@ internal sealed class SwitchModelOptimizer : IOptimizerImplementation private readonly IModelSwitchTheory.CreateNew factory; + /// + /// Initializes a new instance of the class. + /// public SwitchModelOptimizer(IModelSwitchTheory.CreateNew factory) { this.factory = factory; diff --git a/Proxytrace.Application/Optimization/Internal/TheoryValidationService.cs b/Proxytrace.Application/Optimization/Internal/TheoryValidationService.cs index 85fc1fbd3..a09687fc4 100644 --- a/Proxytrace.Application/Optimization/Internal/TheoryValidationService.cs +++ b/Proxytrace.Application/Optimization/Internal/TheoryValidationService.cs @@ -62,6 +62,9 @@ internal sealed class TheoryValidationService : BackgroundService, ITheoryValida /// private readonly ConcurrentDictionary inFlightValidations = new(); + /// + /// Initializes a new instance of the class. + /// public TheoryValidationService( IOptimizationTheoryRepository theories, IOptimizationProposalRepository proposals, @@ -88,6 +91,9 @@ public TheoryValidationService( this.audit = audit; } + /// + /// Submit asynchronously. + /// public async Task SubmitAsync(IOptimizationTheory theory, CancellationToken cancellationToken = default) { // Serialize submissions per project: the quota and dedup checks below are check-then-act, and @@ -116,6 +122,9 @@ public async Task SubmitAsync(IOptimizationTheory theory return new TheorySubmissionResult(TheorySubmissionOutcome.Accepted, persisted); } + /// + /// Reset to proposed asynchronously. + /// public async Task ResetToProposedAsync(Guid theoryId, CancellationToken cancellationToken = default) { var theory = await theories.FindAsync(theoryId, cancellationToken); @@ -149,6 +158,9 @@ public async Task ResetToProposedAsync(Guid theoryId, Cancell return new TheoryResetResult(TheoryResetOutcome.Reset, reset); } + /// + /// Rejects asynchronously. + /// public async Task RejectAsync(Guid theoryId, CancellationToken cancellationToken = default) { var theory = await theories.FindAsync(theoryId, cancellationToken); diff --git a/Proxytrace.Application/Optimization/Internal/UpdateSystemPromptOptimizer.cs b/Proxytrace.Application/Optimization/Internal/UpdateSystemPromptOptimizer.cs index 7b5aff7f7..d56fee453 100644 --- a/Proxytrace.Application/Optimization/Internal/UpdateSystemPromptOptimizer.cs +++ b/Proxytrace.Application/Optimization/Internal/UpdateSystemPromptOptimizer.cs @@ -25,6 +25,9 @@ internal sealed class UpdateSystemPromptOptimizer : IOptimizerImplementation private readonly IAgentRepository agents; private readonly IOptimizerEvidenceBuilder evidenceBuilder; + /// + /// Initializes a new instance of the class. + /// public UpdateSystemPromptOptimizer( ISystemPromptTheory.CreateNew factory, IPromptTemplateRepository prompts, @@ -37,6 +40,9 @@ public UpdateSystemPromptOptimizer( this.evidenceBuilder = evidenceBuilder; } + /// + /// Discover theories. + /// public async Task> DiscoverTheories( ITestRunGroup testRunGroup, IReadOnlyList cohorts, @@ -100,9 +106,15 @@ public async Task> DiscoverTheories( [UsedImplicitly] private record SystemPromptOptimizerOutput { + /// + /// Gets or sets the proposed system prompt. + /// [Description("the full new system prompt")] public required string ProposedSystemPrompt { get; [UsedImplicitly] init; } + /// + /// Gets or sets the rationale. + /// [Description("1-3 sentences explaining what changed and why, citing patterns observed in the failing cases")] public required string Rationale { get; [UsedImplicitly] set; } } diff --git a/Proxytrace.Application/Optimization/Internal/UpdateToolDefinitionOptimizer.cs b/Proxytrace.Application/Optimization/Internal/UpdateToolDefinitionOptimizer.cs index eb67c9f0d..6d2dc505a 100644 --- a/Proxytrace.Application/Optimization/Internal/UpdateToolDefinitionOptimizer.cs +++ b/Proxytrace.Application/Optimization/Internal/UpdateToolDefinitionOptimizer.cs @@ -26,6 +26,9 @@ internal sealed class UpdateToolDefinitionOptimizer : IOptimizerImplementation private readonly IAgentRepository agents; private readonly IOptimizerEvidenceBuilder evidenceBuilder; + /// + /// Initializes a new instance of the class. + /// public UpdateToolDefinitionOptimizer( IToolUpdateTheory.CreateNew factory, IPromptTemplateRepository prompts, @@ -38,6 +41,9 @@ public UpdateToolDefinitionOptimizer( this.evidenceBuilder = evidenceBuilder; } + /// + /// Discover theories. + /// public async Task> DiscoverTheories( ITestRunGroup testRunGroup, IReadOnlyList cohorts, @@ -131,9 +137,15 @@ public async Task> DiscoverTheories( [UsedImplicitly] private record ToolOptimizerOutput { + /// + /// Gets or sets the tools. + /// [Description("the tool definitions that should be changed")] public required IReadOnlyList Tools { get; [UsedImplicitly] init; } + /// + /// Gets or sets the rationale. + /// [Description("1-3 sentences explaining what changed and why")] public required string Rationale { get; [UsedImplicitly] init; } } @@ -141,12 +153,21 @@ private record ToolOptimizerOutput [UsedImplicitly] private record ProposedTool { + /// + /// Gets or sets the name. + /// [Description("the tool name; must match an existing tool name on the agent")] public required string Name { get; [UsedImplicitly] init; } + /// + /// Gets or sets the description. + /// [Description("the proposed tool description")] public required string Description { get; [UsedImplicitly] init; } + /// + /// Gets or sets the json schema. + /// [Description("the proposed JSON schema for the tool's arguments, as a JSON-encoded string (a JSON object with 'type', 'properties', and 'required' fields)")] public required string JsonSchema { get; [UsedImplicitly] init; } } diff --git a/Proxytrace.Application/Optimization/Internal/Validation/AbTestTheoryValidator.cs b/Proxytrace.Application/Optimization/Internal/Validation/AbTestTheoryValidator.cs index bd7a9fc63..91a7a88c6 100644 --- a/Proxytrace.Application/Optimization/Internal/Validation/AbTestTheoryValidator.cs +++ b/Proxytrace.Application/Optimization/Internal/Validation/AbTestTheoryValidator.cs @@ -24,8 +24,14 @@ protected AbTestTheoryValidator( { } + /// + /// Determines whether the validate. + /// public sealed override bool CanValidate(IOptimizationTheory theory) => theory is TTheory; + /// + /// Validates asynchronously. + /// public sealed override async Task ValidateAsync( IOptimizationTheory theory, CancellationToken cancellationToken = default, diff --git a/Proxytrace.Application/Optimization/Internal/Validation/ModelSwitchTheoryValidator.cs b/Proxytrace.Application/Optimization/Internal/Validation/ModelSwitchTheoryValidator.cs index 9ca12f19e..777115a16 100644 --- a/Proxytrace.Application/Optimization/Internal/Validation/ModelSwitchTheoryValidator.cs +++ b/Proxytrace.Application/Optimization/Internal/Validation/ModelSwitchTheoryValidator.cs @@ -17,6 +17,9 @@ internal sealed class ModelSwitchTheoryValidator : TheoryValidatorBase { private readonly IModelSwitchProposal.CreateNew proposalFactory; + /// + /// Initializes a new instance of the class. + /// public ModelSwitchTheoryValidator( IModelSwitchProposal.CreateNew proposalFactory, Lazy testRunnerService, @@ -27,8 +30,14 @@ public ModelSwitchTheoryValidator( this.proposalFactory = proposalFactory; } + /// + /// Determines whether the validate. + /// public override bool CanValidate(IOptimizationTheory theory) => theory is IModelSwitchTheory; + /// + /// Validates asynchronously. + /// public override async Task ValidateAsync( IOptimizationTheory theory, CancellationToken cancellationToken = default, diff --git a/Proxytrace.Application/Optimization/Internal/Validation/SystemPromptTheoryValidator.cs b/Proxytrace.Application/Optimization/Internal/Validation/SystemPromptTheoryValidator.cs index 0b19ac08d..cb93cba13 100644 --- a/Proxytrace.Application/Optimization/Internal/Validation/SystemPromptTheoryValidator.cs +++ b/Proxytrace.Application/Optimization/Internal/Validation/SystemPromptTheoryValidator.cs @@ -20,6 +20,9 @@ internal sealed class SystemPromptTheoryValidator : AbTestTheoryValidator + /// Initializes a new instance of the class. + /// public SystemPromptTheoryValidator( ISystemPromptProposal.CreateNew proposalFactory, IPromptTemplate.Create promptTemplateFactory, diff --git a/Proxytrace.Application/Optimization/Internal/Validation/TheoryValidatorBase.cs b/Proxytrace.Application/Optimization/Internal/Validation/TheoryValidatorBase.cs index dbfe770dd..099d3b767 100644 --- a/Proxytrace.Application/Optimization/Internal/Validation/TheoryValidatorBase.cs +++ b/Proxytrace.Application/Optimization/Internal/Validation/TheoryValidatorBase.cs @@ -38,8 +38,14 @@ protected TheoryValidatorBase( /// How hard a theory has to work to be believed — significance level and A/B samples. protected OptimizationOptions Options { get; } + /// + /// Determines whether the validate. + /// public abstract bool CanValidate(IOptimizationTheory theory); + /// + /// Validates asynchronously. + /// public abstract Task ValidateAsync( IOptimizationTheory theory, CancellationToken cancellationToken = default, diff --git a/Proxytrace.Application/Optimization/Internal/Validation/ToolUpdateTheoryValidator.cs b/Proxytrace.Application/Optimization/Internal/Validation/ToolUpdateTheoryValidator.cs index 49af921f1..6b1cfebe8 100644 --- a/Proxytrace.Application/Optimization/Internal/Validation/ToolUpdateTheoryValidator.cs +++ b/Proxytrace.Application/Optimization/Internal/Validation/ToolUpdateTheoryValidator.cs @@ -17,6 +17,9 @@ internal sealed class ToolUpdateTheoryValidator : AbTestTheoryValidator + /// Initializes a new instance of the class. + /// public ToolUpdateTheoryValidator( IToolUpdateProposal.CreateNew proposalFactory, IAgent.CreateNew agentFactory, diff --git a/Proxytrace.Application/Outliers/Internal/OutlierDetector.cs b/Proxytrace.Application/Outliers/Internal/OutlierDetector.cs index de8d53439..f7f47a22c 100644 --- a/Proxytrace.Application/Outliers/Internal/OutlierDetector.cs +++ b/Proxytrace.Application/Outliers/Internal/OutlierDetector.cs @@ -15,12 +15,18 @@ internal sealed class OutlierDetector : IOutlierDetector private readonly IOutlierSettingsStore settingsStore; private readonly IOutlierBaselineReader baselineReader; + /// + /// Initializes a new instance of the class. + /// public OutlierDetector(IOutlierSettingsStore settingsStore, IOutlierBaselineReader baselineReader) { this.settingsStore = settingsStore; this.baselineReader = baselineReader; } + /// + /// Evaluates asynchronously. + /// public async Task EvaluateAsync( Guid agentId, OutlierMetrics metrics, CancellationToken cancellationToken = default) { diff --git a/Proxytrace.Application/Playground/IPlaygroundService.cs b/Proxytrace.Application/Playground/IPlaygroundService.cs index 91caf4f42..85e3000a5 100644 --- a/Proxytrace.Application/Playground/IPlaygroundService.cs +++ b/Proxytrace.Application/Playground/IPlaygroundService.cs @@ -2,6 +2,9 @@ namespace Proxytrace.Application.Playground; +/// +/// Service that provides playground functionality. +/// public interface IPlaygroundService { IAsyncEnumerable CompleteStreamAsync( diff --git a/Proxytrace.Application/Playground/Internal/PlaygroundCompleteRequest.cs b/Proxytrace.Application/Playground/Internal/PlaygroundCompleteRequest.cs index 19fbb01cb..adc4212c5 100644 --- a/Proxytrace.Application/Playground/Internal/PlaygroundCompleteRequest.cs +++ b/Proxytrace.Application/Playground/Internal/PlaygroundCompleteRequest.cs @@ -1,5 +1,8 @@ namespace Proxytrace.Application.Playground.Internal; +/// +/// Request payload for playground complete operations. +/// public sealed record PlaygroundCompleteRequest( Guid AgentId, Guid EndpointId, @@ -8,6 +11,9 @@ public sealed record PlaygroundCompleteRequest( IReadOnlyList Tools, IReadOnlyList Messages); +/// +/// Represents a playground model parameters. +/// public sealed record PlaygroundModelParameters( double? Temperature, double? TopP, @@ -18,17 +24,26 @@ public sealed record PlaygroundModelParameters( long? Seed, IReadOnlyList? Stop); +/// +/// Represents a playground tool specification. +/// public sealed record PlaygroundToolSpecification( string Name, string Description, IReadOnlyList Arguments); +/// +/// Represents a playground tool argument. +/// public sealed record PlaygroundToolArgument( string Name, string? Description, string Type, bool IsRequired); +/// +/// Represents a playground message. +/// public sealed record PlaygroundMessage( string Role, string Content, @@ -37,4 +52,7 @@ public sealed record PlaygroundMessage( bool ToolSucceeded, string? ToolError); +/// +/// Request payload for playground tool operations. +/// public sealed record PlaygroundToolRequest(string Id, string Name, string Arguments); diff --git a/Proxytrace.Application/Playground/Internal/PlaygroundEvent.cs b/Proxytrace.Application/Playground/Internal/PlaygroundEvent.cs index 1f2615142..35ab93d2a 100644 --- a/Proxytrace.Application/Playground/Internal/PlaygroundEvent.cs +++ b/Proxytrace.Application/Playground/Internal/PlaygroundEvent.cs @@ -1,11 +1,23 @@ namespace Proxytrace.Application.Playground.Internal; +/// +/// Event raised when a playground occurs. +/// public abstract record PlaygroundEvent; +/// +/// Event raised when a token occurs. +/// public sealed record TokenEvent(string Delta) : PlaygroundEvent; +/// +/// Event raised when a tool request occurs. +/// public sealed record ToolRequestEvent(string Id, string Name, string Arguments) : PlaygroundEvent; +/// +/// Event raised when a done occurs. +/// public sealed record DoneEvent( ulong InputTokens, ulong OutputTokens, @@ -14,4 +26,7 @@ public sealed record DoneEvent( decimal? CostEur, string? FinishReason) : PlaygroundEvent; +/// +/// Event raised when a error occurs. +/// public sealed record ErrorEvent(string Message) : PlaygroundEvent; diff --git a/Proxytrace.Application/Playground/Internal/PlaygroundService.cs b/Proxytrace.Application/Playground/Internal/PlaygroundService.cs index 5d630a3d4..c6c378bd8 100644 --- a/Proxytrace.Application/Playground/Internal/PlaygroundService.cs +++ b/Proxytrace.Application/Playground/Internal/PlaygroundService.cs @@ -21,6 +21,9 @@ internal sealed class PlaygroundService : IPlaygroundService private readonly ILogger logger; private readonly bool isDevelopment; + /// + /// Initializes a new instance of the class. + /// public PlaygroundService( IRepository agentRepository, IRepository endpointRepository, @@ -52,6 +55,9 @@ private string DescribeError(Exception exception) : $"An unexpected error occurred. (Error ID: {errorId})"; } + /// + /// Completes the stream asynchronously. + /// public async IAsyncEnumerable CompleteStreamAsync( PlaygroundCompleteRequest request, [EnumeratorCancellation] CancellationToken cancellationToken) @@ -243,13 +249,28 @@ private static IToolArgument CloneArgWithDescription(IToolArgument arg, string? private sealed record OverriddenJsonArgument(string Name, bool IsRequired, JsonElement Json) : IToolArgument { + /// + /// Provides additional functionality. + /// public string? Description => Json.TryGetProperty("description", out var d) && d.ValueKind == JsonValueKind.String ? d.GetString() : null; + /// + /// Gets the type. + /// public Type Type => typeof(object); + /// + /// Gets the default value. + /// public object? DefaultValue => null; + /// + /// Gets the json schema. + /// public string JsonSchema => Json.GetRawText(); + /// + /// Validates. + /// public IEnumerable Validate(ValidationContext validationContext) => []; } diff --git a/Proxytrace.Application/Pricing/Internal/ModelPriceRefresher.cs b/Proxytrace.Application/Pricing/Internal/ModelPriceRefresher.cs index 54b2e7cd4..92c641d08 100644 --- a/Proxytrace.Application/Pricing/Internal/ModelPriceRefresher.cs +++ b/Proxytrace.Application/Pricing/Internal/ModelPriceRefresher.cs @@ -13,6 +13,9 @@ internal sealed class ModelPriceRefresher : IModelPriceRefresher private readonly IModelEndpoint.CreateExisting updateEndpoint; private readonly ILogger logger; + /// + /// Initializes a new instance of the class. + /// public ModelPriceRefresher( IRepository providerRepository, IModelEndpointRepository endpointRepository, @@ -27,6 +30,9 @@ public ModelPriceRefresher( this.logger = logger; } + /// + /// Refreshes the provider asynchronously. + /// public async Task RefreshProviderAsync(IModelProvider provider, CancellationToken cancellationToken = default) { IReadOnlyList existing = await endpointRepository.GetByProviderAsync(provider.Id, cancellationToken); @@ -94,6 +100,9 @@ private static bool IsUsablePrice(ModelPrice price) return true; } + /// + /// Refreshes the all asynchronously. + /// public async Task RefreshAllAsync(CancellationToken cancellationToken = default) { IReadOnlyList providers = await providerRepository.GetAllAsync(cancellationToken); diff --git a/Proxytrace.Application/Pricing/Internal/PriceRefreshService.cs b/Proxytrace.Application/Pricing/Internal/PriceRefreshService.cs index ec958918e..46a29e489 100644 --- a/Proxytrace.Application/Pricing/Internal/PriceRefreshService.cs +++ b/Proxytrace.Application/Pricing/Internal/PriceRefreshService.cs @@ -13,6 +13,9 @@ internal sealed class PriceRefreshService : BackgroundService private readonly IModelPriceRefresher refresher; private readonly ILogger logger; + /// + /// Initializes a new instance of the class. + /// public PriceRefreshService( PriceRefreshConfiguration configuration, IModelPriceRefresher refresher, diff --git a/Proxytrace.Application/Search/Internal/EntityChangeIndexingService.cs b/Proxytrace.Application/Search/Internal/EntityChangeIndexingService.cs index 8446975db..741fe700c 100644 --- a/Proxytrace.Application/Search/Internal/EntityChangeIndexingService.cs +++ b/Proxytrace.Application/Search/Internal/EntityChangeIndexingService.cs @@ -25,6 +25,9 @@ internal sealed class EntityChangeIndexingService : BackgroundService private readonly IReadOnlyDictionary kindByEntityType; private readonly ILogger logger; + /// + /// Initializes a new instance of the class. + /// public EntityChangeIndexingService( IEntityEventService entityEvents, ISearchIndexer indexer, diff --git a/Proxytrace.Application/Search/Internal/LuceneDirectoryFactory.cs b/Proxytrace.Application/Search/Internal/LuceneDirectoryFactory.cs index 450dfb0e8..c4a3f58b5 100644 --- a/Proxytrace.Application/Search/Internal/LuceneDirectoryFactory.cs +++ b/Proxytrace.Application/Search/Internal/LuceneDirectoryFactory.cs @@ -16,6 +16,9 @@ internal sealed class LuceneDirectoryFactory : ILuceneDirectoryFactory private readonly IHostEnvironment? environment; private readonly ILogger? logger; + /// + /// Initializes a new instance of the class. + /// public LuceneDirectoryFactory( SearchConfiguration configuration, IHostEnvironment? environment = null, @@ -26,6 +29,9 @@ public LuceneDirectoryFactory( this.logger = logger; } + /// + /// Opens. + /// public Directory Open() { // No host environment registered (e.g. unit-test container) → in-memory index. diff --git a/Proxytrace.Application/Search/Internal/LuceneIndexWriter.cs b/Proxytrace.Application/Search/Internal/LuceneIndexWriter.cs index 2a7655cde..3481ed93b 100644 --- a/Proxytrace.Application/Search/Internal/LuceneIndexWriter.cs +++ b/Proxytrace.Application/Search/Internal/LuceneIndexWriter.cs @@ -9,6 +9,9 @@ namespace Proxytrace.Application.Search.Internal; internal sealed class LuceneIndexWriter : IDisposable { + /// + /// The version constant value. + /// public const LuceneVersion Version = LuceneVersion.LUCENE_48; private readonly Directory directory; @@ -21,6 +24,9 @@ internal sealed class LuceneIndexWriter : IDisposable // section, so System.Threading.Lock is the correct primitive — IAsyncLock buys no safety here. private readonly Lock commitLock = new(); + /// + /// Initializes a new instance of the class. + /// public LuceneIndexWriter(ILuceneDirectoryFactory factory) : this(factory.Open(), ownsDirectory: true) { } @@ -42,6 +48,9 @@ private LuceneIndexWriter(Directory directory, bool ownsDirectory) internal static LuceneIndexWriter ForTesting(Directory directory) => new(directory, ownsDirectory: false); + /// + /// Upsert. + /// public void Upsert(string id, Document doc) { lock (commitLock) @@ -52,6 +61,9 @@ public void Upsert(string id, Document doc) } } + /// + /// Deletes. + /// public void Delete(string id) { lock (commitLock) @@ -62,6 +74,9 @@ public void Delete(string id) } } + /// + /// Deletes the by query. + /// public void DeleteByQuery(Query query) { lock (commitLock) @@ -72,6 +87,9 @@ public void DeleteByQuery(Query query) } } + /// + /// Upsert deferred. + /// public void UpsertDeferred(string id, Document doc) { lock (commitLock) @@ -80,6 +98,9 @@ public void UpsertDeferred(string id, Document doc) } } + /// + /// Deletes the deferred. + /// public void DeleteDeferred(string id) { lock (commitLock) @@ -88,6 +109,9 @@ public void DeleteDeferred(string id) } } + /// + /// Commits the and refresh. + /// public void CommitAndRefresh() { lock (commitLock) @@ -97,6 +121,9 @@ public void CommitAndRefresh() } } + /// + /// Acquires the reader. + /// public AcquiredReader AcquireReader() { lock (commitLock) @@ -107,6 +134,9 @@ public AcquiredReader AcquireReader() } } + /// + /// Releases all resources used by the current instance. + /// public void Dispose() { searcherManager.Dispose(); @@ -122,15 +152,27 @@ internal sealed class AcquiredReader : IDisposable private readonly SearcherManager manager; private readonly IndexSearcher searcher; + /// + /// Initializes a new instance of the class. + /// public AcquiredReader(SearcherManager manager, IndexSearcher searcher) { this.manager = manager; this.searcher = searcher; } + /// + /// Gets the searcher. + /// public IndexSearcher Searcher => searcher; + /// + /// Gets the index reader. + /// public IndexReader IndexReader => searcher.IndexReader; + /// + /// Releases all resources used by the current instance. + /// public void Dispose() => manager.Release(searcher); } } diff --git a/Proxytrace.Application/Search/Internal/LuceneIndexingService.cs b/Proxytrace.Application/Search/Internal/LuceneIndexingService.cs index 78cb858ba..e4afeeeb8 100644 --- a/Proxytrace.Application/Search/Internal/LuceneIndexingService.cs +++ b/Proxytrace.Application/Search/Internal/LuceneIndexingService.cs @@ -29,6 +29,9 @@ internal sealed class LuceneIndexingService : BackgroundService, ISearchIndexer private TaskCompletionSource idleSignal = CreateIdleSignal(); private int pendingCount; + /// + /// Initializes a new instance of the class. + /// public LuceneIndexingService( LuceneIndexWriter writer, IServiceScopeFactory scopeFactory, @@ -46,6 +49,9 @@ public LuceneIndexingService( private int started; + /// + /// Starts asynchronously. + /// public override Task StartAsync(CancellationToken cancellationToken) { if (Interlocked.Exchange(ref started, 1) != 0) @@ -55,12 +61,21 @@ public override Task StartAsync(CancellationToken cancellationToken) return base.StartAsync(cancellationToken); } + /// + /// Indexes asynchronously. + /// public Task IndexAsync(SearchKind kind, Guid projectId, Guid entityId, CancellationToken cancellationToken = default) => EnqueueAsync(new IndexRequest(kind, entityId, Remove: false), cancellationToken); + /// + /// Removes asynchronously. + /// public Task RemoveAsync(SearchKind kind, Guid entityId, CancellationToken cancellationToken = default) => EnqueueAsync(new IndexRequest(kind, entityId, Remove: true), cancellationToken); + /// + /// Reindexes the project asynchronously. + /// public async Task ReindexProjectAsync(Guid projectId, CancellationToken cancellationToken = default) { using var _ = reindexTracker.BeginReindex(projectId); @@ -98,6 +113,9 @@ private static IReadOnlyDictionary ResolveMappers(I .GroupBy(m => m.Kind) .ToDictionary(g => g.Key, g => g.First()); + /// + /// Flushes asynchronously. + /// public Task FlushAsync(CancellationToken cancellationToken = default) { Task signal; diff --git a/Proxytrace.Application/Search/Internal/LuceneSearchIndexStatistics.cs b/Proxytrace.Application/Search/Internal/LuceneSearchIndexStatistics.cs index 622b61ac5..d3d915eb9 100644 --- a/Proxytrace.Application/Search/Internal/LuceneSearchIndexStatistics.cs +++ b/Proxytrace.Application/Search/Internal/LuceneSearchIndexStatistics.cs @@ -8,11 +8,17 @@ internal sealed class LuceneSearchIndexStatistics : ISearchIndexStatistics { private readonly LuceneIndexWriter writer; + /// + /// Initializes a new instance of the class. + /// public LuceneSearchIndexStatistics(LuceneIndexWriter writer) { this.writer = writer; } + /// + /// Counts asynchronously. + /// public Task CountAsync(Guid projectId, CancellationToken cancellationToken = default) { var query = new TermQuery(new Term(SearchConstants.FieldProjectId, projectId.ToString())); @@ -21,6 +27,9 @@ public Task CountAsync(Guid projectId, CancellationToken cancellationToken return Task.FromResult(top.TotalHits); } + /// + /// Last indexed at asynchronously. + /// public Task LastIndexedAtAsync(Guid projectId, CancellationToken cancellationToken = default) { var query = new TermQuery(new Term(SearchConstants.FieldProjectId, projectId.ToString())); diff --git a/Proxytrace.Application/Search/Internal/LuceneSearchService.cs b/Proxytrace.Application/Search/Internal/LuceneSearchService.cs index 327137c1a..b02d9ded3 100644 --- a/Proxytrace.Application/Search/Internal/LuceneSearchService.cs +++ b/Proxytrace.Application/Search/Internal/LuceneSearchService.cs @@ -15,6 +15,9 @@ internal sealed class LuceneSearchService : ISearchService private readonly SearchConfiguration configuration; private readonly IProjectSearchSettingsResolver settingsResolver; + /// + /// Initializes a new instance of the class. + /// public LuceneSearchService( LuceneIndexWriter writer, SearchConfiguration configuration, @@ -25,6 +28,9 @@ public LuceneSearchService( this.settingsResolver = settingsResolver; } + /// + /// Searches asynchronously. + /// public async Task SearchAsync(Guid projectId, string query, CancellationToken cancellationToken = default) { if (string.IsNullOrWhiteSpace(query)) @@ -154,6 +160,9 @@ public async Task SearchAsync(Guid projectId, string query, Cance return new SearchResults(ordered); } + /// + /// Searches the entity ids asynchronously. + /// public async Task> SearchEntityIdsAsync( Guid projectId, string query, @@ -247,6 +256,9 @@ private static IReadOnlyDictionary ParseMetadata(string? json) } } + /// + /// Gets the recent asynchronously. + /// public Task GetRecentAsync( Guid projectId, IReadOnlyList kinds, diff --git a/Proxytrace.Application/Search/Internal/Mappers/AbstractDocumentMapper.cs b/Proxytrace.Application/Search/Internal/Mappers/AbstractDocumentMapper.cs index 6024c4898..3e3e34b33 100644 --- a/Proxytrace.Application/Search/Internal/Mappers/AbstractDocumentMapper.cs +++ b/Proxytrace.Application/Search/Internal/Mappers/AbstractDocumentMapper.cs @@ -12,8 +12,14 @@ internal abstract class AbstractDocumentMapper : IDocumentMapper private readonly IRepository repository; private readonly ILogger logger; + /// + /// Gets the kind. + /// public abstract SearchKind Kind { get; } + /// + /// Gets the entity type. + /// public Type EntityType => typeof(TDomainEntity); protected AbstractDocumentMapper( @@ -24,12 +30,18 @@ protected AbstractDocumentMapper( this.logger = logger; } + /// + /// Builds asynchronously. + /// public async Task BuildAsync(Guid entityId, CancellationToken cancellationToken) { var entity = await repository.FindAsync(entityId, cancellationToken); return entity is null ? null : GetDocument(entity); } + /// + /// Builds the all for project asynchronously. + /// public async IAsyncEnumerable BuildAllForProjectAsync(Guid projectId, [EnumeratorCancellation] CancellationToken cancellationToken) { IAsyncEnumerable all = repository.EnumerateAsync(cancellationToken) diff --git a/Proxytrace.Application/Search/Internal/Mappers/AgentCallDocumentMapper.cs b/Proxytrace.Application/Search/Internal/Mappers/AgentCallDocumentMapper.cs index 70cbde2c0..1e997bbee 100644 --- a/Proxytrace.Application/Search/Internal/Mappers/AgentCallDocumentMapper.cs +++ b/Proxytrace.Application/Search/Internal/Mappers/AgentCallDocumentMapper.cs @@ -10,8 +10,14 @@ namespace Proxytrace.Application.Search.Internal.Mappers; internal sealed class AgentCallDocumentMapper : AbstractDocumentMapper { + /// + /// Gets the kind. + /// public override SearchKind Kind => SearchKind.AgentCall; + /// + /// Initializes a new instance of the class. + /// public AgentCallDocumentMapper( IRepository repository, ILogger logger) : base(repository, logger) diff --git a/Proxytrace.Application/Search/Internal/Mappers/AgentDocumentMapper.cs b/Proxytrace.Application/Search/Internal/Mappers/AgentDocumentMapper.cs index 04158d359..0da6cb0ee 100644 --- a/Proxytrace.Application/Search/Internal/Mappers/AgentDocumentMapper.cs +++ b/Proxytrace.Application/Search/Internal/Mappers/AgentDocumentMapper.cs @@ -9,8 +9,14 @@ namespace Proxytrace.Application.Search.Internal.Mappers; internal sealed class AgentDocumentMapper : AbstractDocumentMapper { + /// + /// Gets the kind. + /// public override SearchKind Kind => SearchKind.Agent; + /// + /// Initializes a new instance of the class. + /// public AgentDocumentMapper( IRepository repository, ILogger logger) : base(repository, logger) diff --git a/Proxytrace.Application/Search/Internal/Mappers/DocumentBuilder.cs b/Proxytrace.Application/Search/Internal/Mappers/DocumentBuilder.cs index 05e907c0f..a08906db7 100644 --- a/Proxytrace.Application/Search/Internal/Mappers/DocumentBuilder.cs +++ b/Proxytrace.Application/Search/Internal/Mappers/DocumentBuilder.cs @@ -5,6 +5,9 @@ namespace Proxytrace.Application.Search.Internal.Mappers; internal static class DocumentBuilder { + /// + /// Builds. + /// public static Document Build( SearchKind kind, Guid entityId, diff --git a/Proxytrace.Application/Search/Internal/Mappers/EvaluatorDocumentMapper.cs b/Proxytrace.Application/Search/Internal/Mappers/EvaluatorDocumentMapper.cs index 3af305469..49b276642 100644 --- a/Proxytrace.Application/Search/Internal/Mappers/EvaluatorDocumentMapper.cs +++ b/Proxytrace.Application/Search/Internal/Mappers/EvaluatorDocumentMapper.cs @@ -9,8 +9,14 @@ namespace Proxytrace.Application.Search.Internal.Mappers; internal sealed class EvaluatorDocumentMapper : AbstractDocumentMapper { + /// + /// Gets the kind. + /// public override SearchKind Kind => SearchKind.Evaluator; + /// + /// Initializes a new instance of the class. + /// public EvaluatorDocumentMapper( IRepository repository, ILogger logger) : base(repository, logger) diff --git a/Proxytrace.Application/Search/Internal/Mappers/TestCaseDocumentMapper.cs b/Proxytrace.Application/Search/Internal/Mappers/TestCaseDocumentMapper.cs index d8822aa4c..86376ec9a 100644 --- a/Proxytrace.Application/Search/Internal/Mappers/TestCaseDocumentMapper.cs +++ b/Proxytrace.Application/Search/Internal/Mappers/TestCaseDocumentMapper.cs @@ -21,10 +21,19 @@ internal sealed class TestCaseDocumentMapper : IDocumentMapper private readonly ITestSuiteRepository testSuites; private readonly ILogger logger; + /// + /// Gets the kind. + /// public SearchKind Kind => SearchKind.TestCase; + /// + /// Gets the entity type. + /// public Type EntityType => typeof(ITestCase); + /// + /// Initializes a new instance of the class. + /// public TestCaseDocumentMapper( IRepository testCases, ITestSuiteRepository testSuites, @@ -35,6 +44,9 @@ public TestCaseDocumentMapper( this.logger = logger; } + /// + /// Builds asynchronously. + /// public async Task BuildAsync(Guid entityId, CancellationToken cancellationToken) { var tc = await testCases.FindAsync(entityId, cancellationToken); @@ -52,6 +64,9 @@ public TestCaseDocumentMapper( : BuildDocument(tc, suite); } + /// + /// Builds the all for project asynchronously. + /// public async IAsyncEnumerable BuildAllForProjectAsync(Guid projectId, [EnumeratorCancellation] CancellationToken cancellationToken) { var suites = await testSuites.GetByProjectAsync(projectId, cancellationToken); diff --git a/Proxytrace.Application/Search/Internal/Mappers/TestSuiteDocumentMapper.cs b/Proxytrace.Application/Search/Internal/Mappers/TestSuiteDocumentMapper.cs index 0d15cd1d2..34f6c722d 100644 --- a/Proxytrace.Application/Search/Internal/Mappers/TestSuiteDocumentMapper.cs +++ b/Proxytrace.Application/Search/Internal/Mappers/TestSuiteDocumentMapper.cs @@ -9,8 +9,14 @@ namespace Proxytrace.Application.Search.Internal.Mappers; internal sealed class TestSuiteDocumentMapper : AbstractDocumentMapper { + /// + /// Gets the kind. + /// public override SearchKind Kind => SearchKind.TestSuite; + /// + /// Initializes a new instance of the class. + /// public TestSuiteDocumentMapper( IRepository repository, ILogger logger) : base(repository, logger) diff --git a/Proxytrace.Application/Search/Internal/PrefixQueryRewriter.cs b/Proxytrace.Application/Search/Internal/PrefixQueryRewriter.cs index 031e7e84b..2ca658b2c 100644 --- a/Proxytrace.Application/Search/Internal/PrefixQueryRewriter.cs +++ b/Proxytrace.Application/Search/Internal/PrefixQueryRewriter.cs @@ -6,6 +6,9 @@ internal static class PrefixQueryRewriter { private static readonly HashSet ReservedWords = ["AND", "OR", "NOT", "TO"]; + /// + /// Rewrite. + /// public static string Rewrite(string input) { if (string.IsNullOrWhiteSpace(input)) diff --git a/Proxytrace.Application/Search/Internal/ProjectSearchSettingsResolver.cs b/Proxytrace.Application/Search/Internal/ProjectSearchSettingsResolver.cs index e65c850a2..ca50c12ae 100644 --- a/Proxytrace.Application/Search/Internal/ProjectSearchSettingsResolver.cs +++ b/Proxytrace.Application/Search/Internal/ProjectSearchSettingsResolver.cs @@ -13,6 +13,9 @@ internal sealed class ProjectSearchSettingsResolver : IProjectSearchSettingsReso private readonly IProjectSearchSettings.CreateExisting existingFactory; private readonly SearchConfiguration configuration; + /// + /// Initializes a new instance of the class. + /// public ProjectSearchSettingsResolver( IProjectSearchSettingsRepository repository, IRepository projects, @@ -27,6 +30,9 @@ public ProjectSearchSettingsResolver( this.configuration = configuration; } + /// + /// Gets the or defaults asynchronously. + /// public async Task GetOrDefaultsAsync(Guid projectId, CancellationToken cancellationToken = default) { var existing = await repository.FindByProjectAsync(projectId, cancellationToken); @@ -44,6 +50,9 @@ public async Task GetOrDefaultsAsync(Guid projectId, Can snippetLength: configuration.SnippetMaxChars); } + /// + /// Upsert asynchronously. + /// public async Task UpsertAsync(IProjectSearchSettings settings, CancellationToken cancellationToken = default) { var existing = await repository.FindByProjectAsync(settings.Project.Id, cancellationToken); diff --git a/Proxytrace.Application/Search/Internal/QuerySanitizer.cs b/Proxytrace.Application/Search/Internal/QuerySanitizer.cs index 68d4ce878..473305b60 100644 --- a/Proxytrace.Application/Search/Internal/QuerySanitizer.cs +++ b/Proxytrace.Application/Search/Internal/QuerySanitizer.cs @@ -5,6 +5,9 @@ namespace Proxytrace.Application.Search.Internal; internal static class QuerySanitizer { private static readonly char[] Special = + /// + /// Escape. + /// ['+', '-', '&', '|', '!', '(', ')', '{', '}', '[', ']', '^', '"', '~', ':', '\\', '/', '?']; public static string Escape(string input) diff --git a/Proxytrace.Application/Search/Internal/ReindexStateTracker.cs b/Proxytrace.Application/Search/Internal/ReindexStateTracker.cs index 16833ed6c..f7da15cd4 100644 --- a/Proxytrace.Application/Search/Internal/ReindexStateTracker.cs +++ b/Proxytrace.Application/Search/Internal/ReindexStateTracker.cs @@ -7,8 +7,14 @@ internal sealed class ReindexStateTracker : IReindexStateTracker { private readonly ConcurrentDictionary active = new(); + /// + /// Determines whether the reindexing. + /// public bool IsReindexing(Guid projectId) => active.ContainsKey(projectId); + /// + /// Begins the reindex. + /// public IDisposable BeginReindex(Guid projectId) { active[projectId] = 0; @@ -21,12 +27,18 @@ private sealed class Releaser : IDisposable private readonly Guid projectId; private int disposed; + /// + /// Initializes a new instance of the class. + /// public Releaser(ReindexStateTracker owner, Guid projectId) { this.owner = owner; this.projectId = projectId; } + /// + /// Releases all resources used by the current instance. + /// public void Dispose() { if (Interlocked.Exchange(ref disposed, 1) == 0) diff --git a/Proxytrace.Application/Search/Internal/SearchConstants.cs b/Proxytrace.Application/Search/Internal/SearchConstants.cs index c8433a46d..b9ca1320b 100644 --- a/Proxytrace.Application/Search/Internal/SearchConstants.cs +++ b/Proxytrace.Application/Search/Internal/SearchConstants.cs @@ -2,15 +2,45 @@ namespace Proxytrace.Application.Search.Internal; internal static class SearchConstants { + /// + /// The field id constant value. + /// public const string FieldId = "id"; + /// + /// The field kind constant value. + /// public const string FieldKind = "kind"; + /// + /// The field entity id constant value. + /// public const string FieldEntityId = "entityId"; + /// + /// The field project id constant value. + /// public const string FieldProjectId = "projectId"; + /// + /// The field created at constant value. + /// public const string FieldCreatedAt = "createdAt"; + /// + /// The field title constant value. + /// public const string FieldTitle = "title"; + /// + /// The field body constant value. + /// public const string FieldBody = "body"; + /// + /// The field boosted body constant value. + /// public const string FieldBoostedBody = "boostedBody"; + /// + /// The field metadata constant value. + /// public const string FieldMetadata = "metadata"; + /// + /// The boosted body boost constant value. + /// public const float BoostedBodyBoost = 2.0f; } diff --git a/Proxytrace.Application/Search/Internal/TraceIndexPrunerService.cs b/Proxytrace.Application/Search/Internal/TraceIndexPrunerService.cs index 02d9095aa..8ad49695b 100644 --- a/Proxytrace.Application/Search/Internal/TraceIndexPrunerService.cs +++ b/Proxytrace.Application/Search/Internal/TraceIndexPrunerService.cs @@ -12,6 +12,9 @@ internal sealed class TraceIndexPrunerService : BackgroundService private readonly SearchConfiguration configuration; private readonly ILogger logger; + /// + /// Initializes a new instance of the class. + /// public TraceIndexPrunerService( LuceneIndexWriter writer, SearchConfiguration configuration, diff --git a/Proxytrace.Application/Search/SearchConfiguration.cs b/Proxytrace.Application/Search/SearchConfiguration.cs index f8384f8d3..a6d53b356 100644 --- a/Proxytrace.Application/Search/SearchConfiguration.cs +++ b/Proxytrace.Application/Search/SearchConfiguration.cs @@ -1,10 +1,28 @@ namespace Proxytrace.Application.Search; +/// +/// Configuration for search. +/// public sealed record SearchConfiguration { + /// + /// Gets or sets the index path. + /// public string IndexPath { get; init; } = "searchindex"; + /// + /// Gets or sets the trace retention days. + /// public int TraceRetentionDays { get; init; } = 30; + /// + /// Gets or sets the pruner interval hours. + /// public int PrunerIntervalHours { get; init; } = 6; + /// + /// Gets or sets the hits per kind. + /// public int HitsPerKind { get; init; } = 5; + /// + /// Gets or sets the snippet max chars. + /// public int SnippetMaxChars { get; init; } = 160; } diff --git a/Proxytrace.Application/Setup/ISetupService.cs b/Proxytrace.Application/Setup/ISetupService.cs index 5958f1b4f..bba233e7b 100644 --- a/Proxytrace.Application/Setup/ISetupService.cs +++ b/Proxytrace.Application/Setup/ISetupService.cs @@ -2,6 +2,9 @@ namespace Proxytrace.Application.Setup; +/// +/// Service that provides setup functionality. +/// public interface ISetupService { Task CompleteAsync(SetupInput input, CancellationToken cancellationToken = default); @@ -15,12 +18,18 @@ public interface ISetupService Task AnyUsersExistAsync(CancellationToken cancellationToken = default); } +/// +/// Represents a provider connection input. +/// public record ProviderConnectionInput( string ProviderName, Uri ProviderEndpoint, string ProviderUpstreamApiKey, ModelProviderKind ProviderKind); +/// +/// Represents a setup input. +/// public record SetupInput( string ProviderName, Uri ProviderEndpoint, @@ -29,9 +38,15 @@ public record SetupInput( string ModelName, string ProjectName); +/// +/// Encapsulates the result of a setup operation. +/// public record SetupResult( Guid ProviderId, Guid EndpointId, Guid ProjectId); +/// +/// Encapsulates the result of a first admin operation. +/// public record FirstAdminResult(Guid UserId, string Token, DateTimeOffset ExpiresAt); diff --git a/Proxytrace.Application/Setup/Internal/SetupService.cs b/Proxytrace.Application/Setup/Internal/SetupService.cs index b674e2cad..f721e7b05 100644 --- a/Proxytrace.Application/Setup/Internal/SetupService.cs +++ b/Proxytrace.Application/Setup/Internal/SetupService.cs @@ -34,6 +34,9 @@ internal class SetupService : ISetupService private readonly ILicenseService license; private readonly IModelPriceRefresher priceRefresher; + /// + /// Initializes a new instance of the class. + /// public SetupService( IRepository providers, IModelRepository models, @@ -70,6 +73,9 @@ public SetupService( this.priceRefresher = priceRefresher; } + /// + /// Any users exist asynchronously. + /// public async Task AnyUsersExistAsync(CancellationToken cancellationToken = default) { var count = await users.CountAsync(cancellationToken); @@ -86,6 +92,9 @@ public async Task AnyUsersExistAsync(CancellationToken cancellationToken = return count > 0; } + /// + /// Creates the first admin asynchronously. + /// public async Task CreateFirstAdminAsync(string email, string password, CancellationToken cancellationToken = default) { if (await AnyUsersExistAsync(cancellationToken)) @@ -99,6 +108,9 @@ public async Task CreateFirstAdminAsync(string email, string p return new FirstAdminResult(saved.Id, issued.Token, issued.ExpiresAt); } + /// + /// Completes asynchronously. + /// public async Task CompleteAsync(SetupInput input, CancellationToken cancellationToken = default) { // Taken outside the transaction, and held for the whole of it: the "already completed" test @@ -148,11 +160,17 @@ public async Task CompleteAsync(SetupInput input, CancellationToken return result; } + /// + /// Test provider connection asynchronously. + /// public Task TestProviderConnectionAsync(ProviderConnectionInput input, CancellationToken cancellationToken = default) => CreateProvider(input) .CreateClient() .VerifyConnectionAsync(cancellationToken); + /// + /// Lists the provider models asynchronously. + /// public async Task> ListProviderModelsAsync(ProviderConnectionInput input, CancellationToken cancellationToken = default) { IModelProvider provider = CreateProvider(input); diff --git a/Proxytrace.Application/Statistics/DashboardCacheOptions.cs b/Proxytrace.Application/Statistics/DashboardCacheOptions.cs index 5905ced2e..24b20135b 100644 --- a/Proxytrace.Application/Statistics/DashboardCacheOptions.cs +++ b/Proxytrace.Application/Statistics/DashboardCacheOptions.cs @@ -16,5 +16,8 @@ public sealed record DashboardCacheOptions /// public double TtlSeconds { get; init; } = 10d; + /// + /// Gets the ttl. + /// public TimeSpan Ttl => TimeSpan.FromSeconds(TtlSeconds); } diff --git a/Proxytrace.Application/Statistics/Internal/AbstractStatsProjector.cs b/Proxytrace.Application/Statistics/Internal/AbstractStatsProjector.cs index 9dbe0e9fd..06a8b56c3 100644 --- a/Proxytrace.Application/Statistics/Internal/AbstractStatsProjector.cs +++ b/Proxytrace.Application/Statistics/Internal/AbstractStatsProjector.cs @@ -9,6 +9,9 @@ internal abstract class AbstractStatsProjector : IStatsPr private readonly IStatsWriter writer; private readonly IRepository repository; + /// + /// Gets the entity type. + /// public Type EntityType => typeof(TDomainEntity); protected AbstractStatsProjector( @@ -19,6 +22,9 @@ protected AbstractStatsProjector( this.repository = repository; } + /// + /// Project asynchronously. + /// public async Task ProjectAsync(Guid entityId, CancellationToken cancellationToken) { TDomainEntity? entity = await repository.FindAsync(entityId, cancellationToken); diff --git a/Proxytrace.Application/Statistics/Internal/AgentStatistics.cs b/Proxytrace.Application/Statistics/Internal/AgentStatistics.cs index ecdd3f624..48b8d45ea 100644 --- a/Proxytrace.Application/Statistics/Internal/AgentStatistics.cs +++ b/Proxytrace.Application/Statistics/Internal/AgentStatistics.cs @@ -14,6 +14,9 @@ internal class AgentStatistics : IAgentStatistics private readonly ITestSuiteRepository testSuites; private readonly IOptimizationProposalRepository proposals; + /// + /// Initializes a new instance of the class. + /// public AgentStatistics( IStatsReader runStats, IAgentCallStatsReader callStats, @@ -26,6 +29,9 @@ public AgentStatistics( this.proposals = proposals; } + /// + /// Gets the agent overview asynchronously. + /// public async Task GetAgentOverviewAsync(Guid agentId, DateTimeOffset from, DateTimeOffset to, StatisticsBucket bucket, CancellationToken cancellationToken = default) { Task<(IReadOnlyList Series, AgentTimeSummary Summary)> windowTask @@ -46,6 +52,9 @@ public async Task GetAgentOverviewAsync(Guid agentId, DateTim Counts: countsTask.Result); } + /// + /// Gets the agent distributions asynchronously. + /// public Task GetAgentDistributionsAsync(Guid agentId, DateTimeOffset from, DateTimeOffset to, CancellationToken cancellationToken = default) => callStats.GetAgentDistributionsAsync(agentId, from, to, cancellationToken); diff --git a/Proxytrace.Application/Statistics/Internal/DashboardStatistics.cs b/Proxytrace.Application/Statistics/Internal/DashboardStatistics.cs index f55376fda..3a64e1573 100644 --- a/Proxytrace.Application/Statistics/Internal/DashboardStatistics.cs +++ b/Proxytrace.Application/Statistics/Internal/DashboardStatistics.cs @@ -43,6 +43,9 @@ internal class DashboardStatistics : IDashboardStatistics // entry is only ever requested — and therefore only ever served — for admins. private readonly ConcurrentDictionary viewCache = new(); + /// + /// Initializes a new instance of the class. + /// public DashboardStatistics( ITestRunStatsReader runStats, IAgentCallStatsReader callStats, @@ -63,6 +66,9 @@ public DashboardStatistics( this.cacheOptions = cacheOptions; } + /// + /// Gets the dashboard view asynchronously. + /// public async Task GetDashboardViewAsync(StatisticsFilter filter, int recentTraceCount, int agentLimit, CancellationToken cancellationToken = default) { TimeSpan ttl = cacheOptions.Ttl; @@ -214,12 +220,21 @@ await Task.WhenAll( Pulse: pulseTask.Result); } + /// + /// Gets the agent breakdown asynchronously. + /// public Task> GetAgentBreakdownAsync(StatisticsFilter filter, CancellationToken cancellationToken = default) => callStats.GetAgentBreakdownAsync(filter, cancellationToken); + /// + /// Gets the latency asynchronously. + /// public Task> GetLatencyAsync(StatisticsFilter filter, CancellationToken cancellationToken = default) => callStats.GetLatencyAsync(filter, cancellationToken); + /// + /// Gets the anomaly counts by agent asynchronously. + /// public Task> GetAnomalyCountsByAgentAsync(StatisticsFilter filter, StatisticsBucket bucket, CancellationToken cancellationToken = default) => callStats.GetAnomalyCountsByAgentAsync(filter, bucket, cancellationToken); diff --git a/Proxytrace.Application/Statistics/Internal/Worker/StatisticsBackfillHostedService.cs b/Proxytrace.Application/Statistics/Internal/Worker/StatisticsBackfillHostedService.cs index 0e657f1c7..b7bde70d2 100644 --- a/Proxytrace.Application/Statistics/Internal/Worker/StatisticsBackfillHostedService.cs +++ b/Proxytrace.Application/Statistics/Internal/Worker/StatisticsBackfillHostedService.cs @@ -20,6 +20,9 @@ internal class StatisticsBackfillHostedService : IHostedService private CancellationTokenSource? cts; private Task? backfillTask; + /// + /// Initializes a new instance of the class. + /// public StatisticsBackfillHostedService( ITestRunRepository testRuns, IStatsReader runStatsReader, @@ -32,6 +35,9 @@ public StatisticsBackfillHostedService( this.logger = logger; } + /// + /// Starts asynchronously. + /// public Task StartAsync(CancellationToken cancellationToken) { cts = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken); @@ -39,6 +45,9 @@ public Task StartAsync(CancellationToken cancellationToken) return Task.CompletedTask; } + /// + /// Stops asynchronously. + /// public async Task StopAsync(CancellationToken cancellationToken) { if (backfillTask is null) diff --git a/Proxytrace.Application/Statistics/Internal/Worker/StatisticsHostedService.cs b/Proxytrace.Application/Statistics/Internal/Worker/StatisticsHostedService.cs index a2f06eded..e38c7807c 100644 --- a/Proxytrace.Application/Statistics/Internal/Worker/StatisticsHostedService.cs +++ b/Proxytrace.Application/Statistics/Internal/Worker/StatisticsHostedService.cs @@ -13,6 +13,9 @@ internal class StatisticsHostedService : BackgroundService private readonly IReadOnlyDictionary> projectorsByType; private readonly ILogger logger; + /// + /// Initializes a new instance of the class. + /// public StatisticsHostedService( IEntityEventService entityEvents, IEnumerable projectors, diff --git a/Proxytrace.Application/Statistics/TestRun/Internal/TestRunStatsProjector.cs b/Proxytrace.Application/Statistics/TestRun/Internal/TestRunStatsProjector.cs index 79b5eace7..f18f442d3 100644 --- a/Proxytrace.Application/Statistics/TestRun/Internal/TestRunStatsProjector.cs +++ b/Proxytrace.Application/Statistics/TestRun/Internal/TestRunStatsProjector.cs @@ -10,6 +10,9 @@ namespace Proxytrace.Application.Statistics.TestRun.Internal; internal sealed class TestRunStatsProjector : AbstractStatsProjector { + /// + /// Initializes a new instance of the class. + /// public TestRunStatsProjector(IStatsWriter writer, IRepository repository) : base(writer, repository) { diff --git a/Proxytrace.Application/Streaming/ICustomAnomalyBroadcaster.cs b/Proxytrace.Application/Streaming/ICustomAnomalyBroadcaster.cs index 11ac7cf1a..edf4783b1 100644 --- a/Proxytrace.Application/Streaming/ICustomAnomalyBroadcaster.cs +++ b/Proxytrace.Application/Streaming/ICustomAnomalyBroadcaster.cs @@ -2,6 +2,9 @@ namespace Proxytrace.Application.Streaming; +/// +/// Event raised when a anomaly flagged occurs. +/// public record AnomalyFlaggedEvent( Guid AgentCallId, Guid AgentId, @@ -10,6 +13,9 @@ public record AnomalyFlaggedEvent( string DetectorName, bool Blocked = false); +/// +/// Broadcasts custom anomaly events. +/// public interface ICustomAnomalyBroadcaster { ChannelReader Subscribe(CancellationToken cancellationToken); diff --git a/Proxytrace.Application/Streaming/INotificationBroadcaster.cs b/Proxytrace.Application/Streaming/INotificationBroadcaster.cs index 99a2aeb90..4f28907bb 100644 --- a/Proxytrace.Application/Streaming/INotificationBroadcaster.cs +++ b/Proxytrace.Application/Streaming/INotificationBroadcaster.cs @@ -10,6 +10,9 @@ namespace Proxytrace.Application.Streaming; /// public abstract record NotificationEvent(Guid Id, Guid? ProjectId); +/// +/// Event raised when a notification created occurs. +/// public record NotificationCreatedEvent( Guid Id, Guid? ProjectId, @@ -22,6 +25,9 @@ public record NotificationCreatedEvent( Guid? TargetId, DateTimeOffset CreatedAt) : NotificationEvent(Id, ProjectId) { + /// + /// Creates. + /// public static NotificationCreatedEvent Create(INotification notification) => new( notification.Id, @@ -43,6 +49,9 @@ public record NotificationStatusChangedEvent( NotificationStatus Status, DateTimeOffset UpdatedAt) : NotificationEvent(Id, ProjectId) { + /// + /// Creates. + /// public static NotificationStatusChangedEvent Create(INotification notification) => new( notification.Id, @@ -51,6 +60,9 @@ public static NotificationStatusChangedEvent Create(INotification notification) notification.UpdatedAt); } +/// +/// Broadcasts notification events. +/// public interface INotificationBroadcaster { /// diff --git a/Proxytrace.Application/Streaming/IProposalBroadcaster.cs b/Proxytrace.Application/Streaming/IProposalBroadcaster.cs index 8ee727fba..6a9bc65fb 100644 --- a/Proxytrace.Application/Streaming/IProposalBroadcaster.cs +++ b/Proxytrace.Application/Streaming/IProposalBroadcaster.cs @@ -9,6 +9,9 @@ namespace Proxytrace.Application.Streaming; /// public abstract record ProposalEvent(Guid Id, Guid AgentId); +/// +/// Event raised when a proposal created occurs. +/// public record ProposalCreatedEvent( Guid Id, Guid AgentId, @@ -17,6 +20,9 @@ public record ProposalCreatedEvent( string Rationale, DateTimeOffset CreatedAt) : ProposalEvent(Id, AgentId) { + /// + /// Creates. + /// public static ProposalCreatedEvent Create(IOptimizationProposal proposal) => new( proposal.Id, @@ -42,6 +48,9 @@ public record ProposalStatusChangedEvent( bool? AdoptedManually, DateTimeOffset UpdatedAt) : ProposalEvent(Id, AgentId) { + /// + /// Creates. + /// public static ProposalStatusChangedEvent Create(IOptimizationProposal proposal) => new( proposal.Id, @@ -55,6 +64,9 @@ public static ProposalStatusChangedEvent Create(IOptimizationProposal proposal) proposal.UpdatedAt); } +/// +/// Broadcasts proposal events. +/// public interface IProposalBroadcaster { /// diff --git a/Proxytrace.Application/Streaming/ITestResultBroadcaster.cs b/Proxytrace.Application/Streaming/ITestResultBroadcaster.cs index a60ef33ff..f630e5eb7 100644 --- a/Proxytrace.Application/Streaming/ITestResultBroadcaster.cs +++ b/Proxytrace.Application/Streaming/ITestResultBroadcaster.cs @@ -7,6 +7,9 @@ namespace Proxytrace.Application.Streaming; +/// +/// Represents a evaluation event data. +/// public record EvaluationEventData( Guid EvaluatorId, EvaluatorKind EvaluatorKind, @@ -15,24 +18,39 @@ public record EvaluationEventData( string? Reasoning, string? ErrorMessage); +/// +/// Event raised when a test run occurs. +/// public abstract record TestRunEvent(Guid RunId, Guid GroupId); +/// +/// Event raised when a test case started occurs. +/// public record TestCaseStartedEvent( Guid RunId, Guid GroupId, Guid TestCaseId) : TestRunEvent(RunId, GroupId); +/// +/// Event raised when a inference done occurs. +/// public record InferenceDoneEvent( Guid RunId, Guid GroupId, Guid TestCaseId) : TestRunEvent(RunId, GroupId); +/// +/// Event raised when a evaluation arrived occurs. +/// public record EvaluationArrivedEvent( Guid RunId, Guid GroupId, Guid TestCaseId, EvaluationEventData Evaluation) : TestRunEvent(RunId, GroupId); +/// +/// Event raised when a test result arrived occurs. +/// public record TestResultArrivedEvent( Guid RunId, Guid GroupId, @@ -45,6 +63,9 @@ public record TestResultArrivedEvent( long? TokensOut, long? CachedTokensIn) : TestRunEvent(RunId, GroupId) { + /// + /// Creates. + /// public static TestResultArrivedEvent Create(ITestRun run, ITestResult result) { // Per-case cost/tokens ride along so the live run view can sum a running run's totals as each @@ -71,12 +92,18 @@ public static TestResultArrivedEvent Create(ITestRun run, ITestResult result) } } +/// +/// Event raised when a run complete occurs. +/// public record RunCompleteEvent( Guid RunId, Guid GroupId, TestRunStatus Status, DateTimeOffset? CompletedAt) : TestRunEvent(RunId, GroupId) { + /// + /// Creates. + /// public static RunCompleteEvent Create(ITestRun testRun) => new(testRun.Id, testRun.Group.Id, testRun.Status, testRun.CompletedAt); } @@ -90,10 +117,16 @@ public record GroupRunCompleteEvent( TestRunStatus GroupStatus, DateTimeOffset? GroupCompletedAt) : TestRunEvent(Guid.Empty, GroupId) { + /// + /// Creates. + /// public static GroupRunCompleteEvent Create(ITestRunGroup group) => new(group.Id, group.Status, group.CompletedAt); } +/// +/// Broadcasts test result events. +/// public interface ITestResultBroadcaster { /// Subscribe to real-time events for a single run. diff --git a/Proxytrace.Application/Streaming/ITheoryBroadcaster.cs b/Proxytrace.Application/Streaming/ITheoryBroadcaster.cs index e09559e6b..ed2cedc40 100644 --- a/Proxytrace.Application/Streaming/ITheoryBroadcaster.cs +++ b/Proxytrace.Application/Streaming/ITheoryBroadcaster.cs @@ -5,6 +5,9 @@ namespace Proxytrace.Application.Streaming; +/// +/// Event raised when a theory status changed occurs. +/// public record TheoryStatusChangedEvent( Guid Id, Guid AgentId, @@ -16,6 +19,9 @@ public record TheoryStatusChangedEvent( Guid? ResultingProposalId, DateTimeOffset UpdatedAt) { + /// + /// Creates. + /// public static TheoryStatusChangedEvent Create(IOptimizationTheory theory) => new( theory.Id, @@ -29,6 +35,9 @@ public static TheoryStatusChangedEvent Create(IOptimizationTheory theory) theory.UpdatedAt); } +/// +/// Broadcasts theory events. +/// public interface ITheoryBroadcaster { /// diff --git a/Proxytrace.Application/Streaming/ITraceBroadcaster.cs b/Proxytrace.Application/Streaming/ITraceBroadcaster.cs index 254453409..d1bc69d06 100644 --- a/Proxytrace.Application/Streaming/ITraceBroadcaster.cs +++ b/Proxytrace.Application/Streaming/ITraceBroadcaster.cs @@ -3,6 +3,9 @@ namespace Proxytrace.Application.Streaming; +/// +/// Event raised when a trace created occurs. +/// public record TraceCreatedEvent( Guid Id, Guid AgentId, @@ -14,6 +17,9 @@ public record TraceCreatedEvent( Guid? ConversationId, Guid? SessionId) { + /// + /// Creates. + /// public static TraceCreatedEvent Create(IAgentCall call) => new( call.Id, @@ -27,6 +33,9 @@ public static TraceCreatedEvent Create(IAgentCall call) call.SessionId); } +/// +/// Broadcasts trace events. +/// public interface ITraceBroadcaster { ChannelReader Subscribe(CancellationToken cancellationToken); diff --git a/Proxytrace.Application/Streaming/Internal/AgentScopedBroadcaster.cs b/Proxytrace.Application/Streaming/Internal/AgentScopedBroadcaster.cs index 2e43f0307..319a113ce 100644 --- a/Proxytrace.Application/Streaming/Internal/AgentScopedBroadcaster.cs +++ b/Proxytrace.Application/Streaming/Internal/AgentScopedBroadcaster.cs @@ -20,6 +20,9 @@ internal abstract class AgentScopedBroadcaster : IDisposable /// Extracts the agent id an event should be delivered to. protected abstract Guid KeyOf(TEvent evt); + /// + /// Subscribes. + /// public ChannelReader Subscribe(Guid agentId, CancellationToken cancellationToken) { var channel = Channel.CreateBounded(new BoundedChannelOptions(100) @@ -63,6 +66,9 @@ private int TotalSubscribers() return total; } + /// + /// Publishes. + /// public void Publish(TEvent evt) { if (!subscribers.TryGetValue(KeyOf(evt), out var agentSubscribers)) @@ -75,6 +81,9 @@ public void Publish(TEvent evt) } } + /// + /// Releases all resources used by the current instance. + /// public void Dispose() { foreach (var (_, agentSubscribers) in subscribers) diff --git a/Proxytrace.Application/Streaming/Internal/CustomAnomalyBroadcaster.cs b/Proxytrace.Application/Streaming/Internal/CustomAnomalyBroadcaster.cs index 6ce5ede13..75b9ae934 100644 --- a/Proxytrace.Application/Streaming/Internal/CustomAnomalyBroadcaster.cs +++ b/Proxytrace.Application/Streaming/Internal/CustomAnomalyBroadcaster.cs @@ -12,6 +12,9 @@ internal class CustomAnomalyBroadcaster : ICustomAnomalyBroadcaster, IDisposable private readonly ConcurrentDictionary> subscribers = new(); + /// + /// Subscribes. + /// public ChannelReader Subscribe(CancellationToken cancellationToken) { if (!cancellationToken.CanBeCanceled) @@ -44,6 +47,9 @@ public ChannelReader Subscribe(CancellationToken cancellati return channel.Reader; } + /// + /// Publishes. + /// public void Publish(AnomalyFlaggedEvent evt) { foreach (var kvp in subscribers) @@ -53,6 +59,9 @@ public void Publish(AnomalyFlaggedEvent evt) } } + /// + /// Releases all resources used by the current instance. + /// public void Dispose() { var writers = subscribers.Values.ToList(); diff --git a/Proxytrace.Application/Streaming/Internal/NotificationBroadcaster.cs b/Proxytrace.Application/Streaming/Internal/NotificationBroadcaster.cs index 278bda29b..84edf01ef 100644 --- a/Proxytrace.Application/Streaming/Internal/NotificationBroadcaster.cs +++ b/Proxytrace.Application/Streaming/Internal/NotificationBroadcaster.cs @@ -13,6 +13,9 @@ internal class NotificationBroadcaster : INotificationBroadcaster, IDisposable private readonly ConcurrentDictionary> subscribers = new(); + /// + /// Subscribes. + /// public ChannelReader Subscribe(CancellationToken cancellationToken) { if (!cancellationToken.CanBeCanceled) @@ -45,6 +48,9 @@ public ChannelReader Subscribe(CancellationToken cancellation return channel.Reader; } + /// + /// Publishes. + /// public void Publish(NotificationEvent evt) { foreach (var kvp in subscribers) @@ -54,6 +60,9 @@ public void Publish(NotificationEvent evt) } } + /// + /// Releases all resources used by the current instance. + /// public void Dispose() { var writers = subscribers.Values.ToList(); diff --git a/Proxytrace.Application/Streaming/Internal/TestResultBroadcaster.cs b/Proxytrace.Application/Streaming/Internal/TestResultBroadcaster.cs index 1dd90b79b..3ee69b826 100644 --- a/Proxytrace.Application/Streaming/Internal/TestResultBroadcaster.cs +++ b/Proxytrace.Application/Streaming/Internal/TestResultBroadcaster.cs @@ -19,9 +19,15 @@ private readonly ConcurrentDictionary>> groupSubscribers = new(); + /// + /// Subscribes. + /// public ChannelReader Subscribe(Guid runId, CancellationToken cancellationToken) => Add(runSubscribers, runId, cancellationToken); + /// + /// Subscribes the to group. + /// public ChannelReader SubscribeToGroup(Guid groupId, CancellationToken cancellationToken) => Add(groupSubscribers, groupId, cancellationToken); @@ -75,12 +81,18 @@ private int TotalSubscribers() return total; } + /// + /// Publishes. + /// public void Publish(TestRunEvent evt) { ForwardToRunSubscribers(evt); ForwardToGroupSubscribers(evt); } + /// + /// Publishes the complete. + /// public void PublishComplete(RunCompleteEvent evt) { if (runSubscribers.TryRemove(evt.RunId, out var bucket)) @@ -97,6 +109,9 @@ public void PublishComplete(RunCompleteEvent evt) ForwardToGroupSubscribers(evt); } + /// + /// Publishes the group complete. + /// public void PublishGroupComplete(GroupRunCompleteEvent evt) { if (!groupSubscribers.TryRemove(evt.GroupId, out var bucket)) @@ -108,6 +123,9 @@ public void PublishGroupComplete(GroupRunCompleteEvent evt) } } + /// + /// Releases all resources used by the current instance. + /// public void Dispose() { foreach (var bucket in runSubscribers.Values) diff --git a/Proxytrace.Application/Streaming/Internal/TraceBroadcaster.cs b/Proxytrace.Application/Streaming/Internal/TraceBroadcaster.cs index 692454b15..355927614 100644 --- a/Proxytrace.Application/Streaming/Internal/TraceBroadcaster.cs +++ b/Proxytrace.Application/Streaming/Internal/TraceBroadcaster.cs @@ -13,6 +13,9 @@ internal class TraceBroadcaster : ITraceBroadcaster, IDisposable private readonly ConcurrentDictionary> traceSubscribers = new(); + /// + /// Subscribes. + /// public ChannelReader Subscribe(CancellationToken cancellationToken) { if (!cancellationToken.CanBeCanceled) @@ -45,6 +48,9 @@ public ChannelReader Subscribe(CancellationToken cancellation return channel.Reader; } + /// + /// Publishes. + /// public void Publish(TraceCreatedEvent evt) { foreach (var kvp in traceSubscribers) @@ -54,6 +60,9 @@ public void Publish(TraceCreatedEvent evt) } } + /// + /// Releases all resources used by the current instance. + /// public void Dispose() { var subscribers = traceSubscribers.Values.ToList(); diff --git a/Proxytrace.Application/TestCase/Internal/ProposalValidator.cs b/Proxytrace.Application/TestCase/Internal/ProposalValidator.cs index d50dfcef7..13d602b32 100644 --- a/Proxytrace.Application/TestCase/Internal/ProposalValidator.cs +++ b/Proxytrace.Application/TestCase/Internal/ProposalValidator.cs @@ -12,6 +12,9 @@ namespace Proxytrace.Application.TestCase.Internal; /// internal static class ProposalValidator { + /// + /// Validates. + /// public static TestCaseProposalSet Validate( SynthesisOutput output, IReadOnlyList conversation) diff --git a/Proxytrace.Application/TestCase/Internal/SynthesisOutput.cs b/Proxytrace.Application/TestCase/Internal/SynthesisOutput.cs index 7417fc8b3..769a1b8b5 100644 --- a/Proxytrace.Application/TestCase/Internal/SynthesisOutput.cs +++ b/Proxytrace.Application/TestCase/Internal/SynthesisOutput.cs @@ -13,15 +13,27 @@ namespace Proxytrace.Application.TestCase.Internal; [UsedImplicitly] internal sealed record SynthesisOutput { + /// + /// Gets or sets the summary. + /// [Description("one sentence describing what this conversation does")] public required string Summary { get; [UsedImplicitly] init; } + /// + /// Gets or sets the proposals. + /// [Description("the test cases worth building, most consequential first; at most 10")] public required IReadOnlyList Proposals { get; [UsedImplicitly] init; } + /// + /// Gets or sets the skipped. + /// [Description("assistant turns you deliberately did not propose, each with the reason why")] public IReadOnlyList? Skipped { get; [UsedImplicitly] init; } + /// + /// Gets or sets the evaluator suggestion. + /// [Description("an agentic judge to add when the destination suite cannot score these cases; omit otherwise")] public SynthesisEvaluatorSuggestion? EvaluatorSuggestion { get; [UsedImplicitly] init; } } @@ -29,24 +41,45 @@ internal sealed record SynthesisOutput [UsedImplicitly] internal sealed record SynthesisProposal { + /// + /// Gets or sets the agent call id. + /// [Description("the agentCallId of the call this case is built from, copied exactly from the transcript")] public required string AgentCallId { get; [UsedImplicitly] init; } + /// + /// Gets or sets the kind. + /// [Description("Promotion to lock in what the agent did, Correction to assert what it should have done")] public required ProposalKind Kind { get; [UsedImplicitly] init; } + /// + /// Gets or sets the title. + /// [Description("a short label for the case, e.g. 'Looks up the order before refunding'")] public required string Title { get; [UsedImplicitly] init; } + /// + /// Gets or sets the rationale. + /// [Description("one or two sentences: why this turn is worth testing, and for a Correction what is wrong")] public required string Rationale { get; [UsedImplicitly] init; } + /// + /// Gets or sets the relevance. + /// [Description("High, Medium or Low — how consequential the decision at this turn is")] public required ProposalRelevance Relevance { get; [UsedImplicitly] init; } + /// + /// Gets or sets the expected content. + /// [Description("for a Correction only: the assistant text the agent should have produced; empty when it should call tools instead")] public string? ExpectedContent { get; [UsedImplicitly] init; } + /// + /// Gets or sets the expected tool requests. + /// [Description("for a Correction only: the tool calls the agent should have made")] public IReadOnlyList? ExpectedToolRequests { get; [UsedImplicitly] init; } } @@ -54,9 +87,15 @@ internal sealed record SynthesisProposal [UsedImplicitly] internal sealed record SynthesisToolRequest { + /// + /// Gets or sets the name. + /// [Description("the tool name; must be one of the tools the agent was offered")] public required string Name { get; [UsedImplicitly] init; } + /// + /// Gets or sets the arguments. + /// [Description("the arguments as a JSON-encoded object string, e.g. {\"order_id\":\"91\"}")] public required string Arguments { get; [UsedImplicitly] init; } } @@ -64,9 +103,15 @@ internal sealed record SynthesisToolRequest [UsedImplicitly] internal sealed record SynthesisSkipped { + /// + /// Gets or sets the agent call id. + /// [Description("the agentCallId of the skipped turn")] public required string AgentCallId { get; [UsedImplicitly] init; } + /// + /// Gets or sets the reason. + /// [Description("why it is not worth a test case, e.g. 'closing summary — grades prose only'")] public required string Reason { get; [UsedImplicitly] init; } } @@ -74,15 +119,27 @@ internal sealed record SynthesisSkipped [UsedImplicitly] internal sealed record SynthesisEvaluatorSuggestion { + /// + /// Gets or sets the name. + /// [Description("a short name for the judge, e.g. 'Refund policy judge'")] public required string Name { get; [UsedImplicitly] init; } + /// + /// Gets or sets the instructions. + /// [Description("the judge's system prompt: what it should check and how it should decide")] public required string Instructions { get; [UsedImplicitly] init; } + /// + /// Gets or sets the reason. + /// [Description("why the destination suite's current evaluators cannot score these cases")] public required string Reason { get; [UsedImplicitly] init; } + /// + /// Gets or sets the target. + /// [Description("Attach to add the judge to the destination suite (it will also score its other cases), or NewSuite to put these cases in a fresh suite instead")] public required EvaluatorSuggestionTarget Target { get; [UsedImplicitly] init; } } diff --git a/Proxytrace.Application/TestCase/Internal/TestCaseSynthesisService.cs b/Proxytrace.Application/TestCase/Internal/TestCaseSynthesisService.cs index 99868f4cb..8e4c0029a 100644 --- a/Proxytrace.Application/TestCase/Internal/TestCaseSynthesisService.cs +++ b/Proxytrace.Application/TestCase/Internal/TestCaseSynthesisService.cs @@ -55,6 +55,9 @@ internal sealed class TestCaseSynthesisService : ITestCaseSynthesisService private readonly IPromptTemplateRepository prompts; private readonly IAgentRepository agents; + /// + /// Initializes a new instance of the class. + /// public TestCaseSynthesisService( IAgentCallRepository calls, IPromptTemplateRepository prompts, @@ -65,6 +68,9 @@ public TestCaseSynthesisService( this.agents = agents; } + /// + /// Synthesize asynchronously. + /// public async Task SynthesizeAsync( IAgentCall origin, ITestSuite? destination, diff --git a/Proxytrace.Application/TestCase/TestCaseProposalSet.cs b/Proxytrace.Application/TestCase/TestCaseProposalSet.cs index 06da6501e..f43e68c6d 100644 --- a/Proxytrace.Application/TestCase/TestCaseProposalSet.cs +++ b/Proxytrace.Application/TestCase/TestCaseProposalSet.cs @@ -32,15 +32,27 @@ public enum ProposalFlag UnknownTool = 1, } +/// +/// Request payload for proposed tool operations. +/// public sealed record ProposedToolRequest(string Name, string Arguments); +/// +/// Represents a proposed expected output. +/// public sealed record ProposedExpectedOutput(string Content, IReadOnlyList ToolRequests); +/// +/// Represents a test case proposal. +/// public sealed record TestCaseProposal { /// The call of the conversation the case is built from; its request becomes the input. public required Guid AgentCallId { get; init; } + /// + /// Gets or sets the kind. + /// public required ProposalKind Kind { get; init; } /// Short label, e.g. "Looks up the order before refunding". @@ -49,11 +61,17 @@ public sealed record TestCaseProposal /// One or two sentences: why this turn is worth testing. public required string Rationale { get; init; } + /// + /// Gets or sets the relevance. + /// public required ProposalRelevance Relevance { get; init; } /// Set for a only; null means promote as-is. public ProposedExpectedOutput? ExpectedOutput { get; init; } + /// + /// Gets or sets the flags. + /// public IReadOnlyList Flags { get; init; } = []; } @@ -70,8 +88,14 @@ public enum EvaluatorSuggestionTarget NewSuite = 1, } +/// +/// Represents a evaluator suggestion. +/// public sealed record EvaluatorSuggestion { + /// + /// Gets or sets the name. + /// public required string Name { get; init; } /// The judge's system prompt / rubric. @@ -80,9 +104,15 @@ public sealed record EvaluatorSuggestion /// Why the destination suite's current evaluators cannot score these proposals. public required string Reason { get; init; } + /// + /// Gets or sets the target. + /// public required EvaluatorSuggestionTarget Target { get; init; } } +/// +/// Represents a test case proposal set. +/// public sealed record TestCaseProposalSet { /// Hard cap on proposals per round. @@ -91,14 +121,29 @@ public sealed record TestCaseProposalSet /// Hard cap on refinement rounds carried into one request. public const int MaxRounds = 5; + /// + /// Gets or sets the summary. + /// public required string Summary { get; init; } + /// + /// Gets or sets the proposals. + /// public required IReadOnlyList Proposals { get; init; } + /// + /// Gets or sets the skipped. + /// public required IReadOnlyList Skipped { get; init; } + /// + /// Gets or sets the evaluator suggestion. + /// public EvaluatorSuggestion? EvaluatorSuggestion { get; init; } + /// + /// Gets the empty. + /// public static TestCaseProposalSet Empty { get; } = new() { Summary = string.Empty, diff --git a/Proxytrace.Application/TestRun/ITestRunnerService.cs b/Proxytrace.Application/TestRun/ITestRunnerService.cs index b93879696..09091895d 100644 --- a/Proxytrace.Application/TestRun/ITestRunnerService.cs +++ b/Proxytrace.Application/TestRun/ITestRunnerService.cs @@ -5,6 +5,9 @@ namespace Proxytrace.Application.TestRun; +/// +/// Service that provides test runner functionality. +/// public interface ITestRunnerService { /// diff --git a/Proxytrace.Application/TestRun/Internal/OrphanedTestRunReaperHostedService.cs b/Proxytrace.Application/TestRun/Internal/OrphanedTestRunReaperHostedService.cs index 770a43965..45240ec7a 100644 --- a/Proxytrace.Application/TestRun/Internal/OrphanedTestRunReaperHostedService.cs +++ b/Proxytrace.Application/TestRun/Internal/OrphanedTestRunReaperHostedService.cs @@ -21,6 +21,9 @@ internal sealed class OrphanedTestRunReaperHostedService : IHostedService private readonly IServiceProvider rootServices; private readonly ILogger logger; + /// + /// Initializes a new instance of the class. + /// public OrphanedTestRunReaperHostedService( IServiceProvider rootServices, ILogger logger) @@ -29,6 +32,9 @@ public OrphanedTestRunReaperHostedService( this.logger = logger; } + /// + /// Starts asynchronously. + /// public async Task StartAsync(CancellationToken cancellationToken) { using var scope = rootServices.CreateScope(); @@ -43,6 +49,9 @@ await ReapAsync( cancellationToken); } + /// + /// Stops asynchronously. + /// public Task StopAsync(CancellationToken cancellationToken) => Task.CompletedTask; /// diff --git a/Proxytrace.Application/TestRun/Internal/TestRunSchedulerService.cs b/Proxytrace.Application/TestRun/Internal/TestRunSchedulerService.cs index 2e9c3b312..ba3c90abb 100644 --- a/Proxytrace.Application/TestRun/Internal/TestRunSchedulerService.cs +++ b/Proxytrace.Application/TestRun/Internal/TestRunSchedulerService.cs @@ -24,6 +24,9 @@ internal sealed class TestRunSchedulerService : BackgroundService private readonly IAgentRepository agents; private readonly ILogger audit; + /// + /// Initializes a new instance of the class. + /// public TestRunSchedulerService( ITestRunScheduleRepository schedules, ITestRunGroupRepository groups, diff --git a/Proxytrace.Application/TestRun/Internal/TestRunnerService.cs b/Proxytrace.Application/TestRun/Internal/TestRunnerService.cs index 94f4637a3..3e6a579b0 100644 --- a/Proxytrace.Application/TestRun/Internal/TestRunnerService.cs +++ b/Proxytrace.Application/TestRun/Internal/TestRunnerService.cs @@ -71,6 +71,9 @@ internal class TestRunnerService : BackgroundService, ITestRunnerService SingleWriter = false, }); + /// + /// Initializes a new instance of the class. + /// public TestRunnerService( ITestResult.CreateNew createTestResult, ITestRun.CreateNew createTestRun, @@ -124,6 +127,9 @@ private async Task ThroughModelCallGateAsync( } } + /// + /// Runs the in foreground asynchronously. + /// public async Task RunInForegroundAsync( ITestSuite suite, IReadOnlyList endpoints, @@ -139,6 +145,9 @@ public async Task RunInForegroundAsync( return await ExecuteGroupAsync(group, customAgent, isSystemTestRun, cancellationToken); } + /// + /// Runs the in background asynchronously. + /// public async Task RunInBackgroundAsync( ITestSuite suite, IReadOnlyList endpoints, @@ -187,6 +196,9 @@ private async Task CreateGroup( return group; } + /// + /// Cancels asynchronously. + /// public async Task CancelAsync(ITestRunGroup group, CancellationToken cancellationToken = default) { // The executing group registers its source under the *group* id (see ExecuteGroupAsync). diff --git a/Proxytrace.Application/TestRun/TestRunnerConfiguration.cs b/Proxytrace.Application/TestRun/TestRunnerConfiguration.cs index 3f9e6235d..845f61cc9 100644 --- a/Proxytrace.Application/TestRun/TestRunnerConfiguration.cs +++ b/Proxytrace.Application/TestRun/TestRunnerConfiguration.cs @@ -1,5 +1,8 @@ namespace Proxytrace.Application.TestRun; +/// +/// Represents a test runner configuration. +/// public sealed class TestRunnerConfiguration { /// diff --git a/Proxytrace.Application/Tracey/Internal/TraceyAgentProvisioner.cs b/Proxytrace.Application/Tracey/Internal/TraceyAgentProvisioner.cs index 3dad67c38..cf85bb258 100644 --- a/Proxytrace.Application/Tracey/Internal/TraceyAgentProvisioner.cs +++ b/Proxytrace.Application/Tracey/Internal/TraceyAgentProvisioner.cs @@ -27,6 +27,9 @@ internal sealed class TraceyAgentProvisioner : ITraceyAgentProvisioner private readonly IPromptTemplate.Create createPrompt; private readonly IModelParameters.Create createParameters; + /// + /// Initializes a new instance of the class. + /// public TraceyAgentProvisioner( IAgentRepository agents, IAgent.CreateNew createAgent, @@ -39,6 +42,9 @@ public TraceyAgentProvisioner( this.createParameters = createParameters; } + /// + /// Ensures the tracey agent asynchronously. + /// public async Task EnsureTraceyAgentAsync(IProject project, CancellationToken cancellationToken = default) { ArgumentNullException.ThrowIfNull(project); diff --git a/Proxytrace.Application/Tracey/Internal/TraceyAgentSeederHostedService.cs b/Proxytrace.Application/Tracey/Internal/TraceyAgentSeederHostedService.cs index a03ebe73e..b3c9c7d95 100644 --- a/Proxytrace.Application/Tracey/Internal/TraceyAgentSeederHostedService.cs +++ b/Proxytrace.Application/Tracey/Internal/TraceyAgentSeederHostedService.cs @@ -17,6 +17,9 @@ internal sealed class TraceyAgentSeederHostedService : IHostedService private readonly IServiceProvider rootServices; private readonly ILogger logger; + /// + /// Initializes a new instance of the class. + /// public TraceyAgentSeederHostedService( IServiceProvider rootServices, ILogger logger) @@ -25,6 +28,9 @@ public TraceyAgentSeederHostedService( this.logger = logger; } + /// + /// Starts asynchronously. + /// public async Task StartAsync(CancellationToken cancellationToken) { using var scope = rootServices.CreateScope(); @@ -46,5 +52,8 @@ public async Task StartAsync(CancellationToken cancellationToken) logger.LogInformation("Tracey seeding complete"); } + /// + /// Stops asynchronously. + /// public Task StopAsync(CancellationToken cancellationToken) => Task.CompletedTask; } diff --git a/Proxytrace.Application/Tracey/Internal/TraceySessionService.cs b/Proxytrace.Application/Tracey/Internal/TraceySessionService.cs index 8b8e7e049..8f85302e1 100644 --- a/Proxytrace.Application/Tracey/Internal/TraceySessionService.cs +++ b/Proxytrace.Application/Tracey/Internal/TraceySessionService.cs @@ -6,11 +6,17 @@ internal sealed class TraceySessionService : ITraceySessionService { private readonly ITraceyAgentProvisioner provisioner; + /// + /// Initializes a new instance of the class. + /// public TraceySessionService(ITraceyAgentProvisioner provisioner) { this.provisioner = provisioner; } + /// + /// Creates the session asynchronously. + /// public async Task CreateSessionAsync(IProject project, CancellationToken cancellationToken = default) { ArgumentNullException.ThrowIfNull(project); diff --git a/Proxytrace.Application/Updates/Internal/UpdateCheckService.cs b/Proxytrace.Application/Updates/Internal/UpdateCheckService.cs index 0cec219d9..de7faba76 100644 --- a/Proxytrace.Application/Updates/Internal/UpdateCheckService.cs +++ b/Proxytrace.Application/Updates/Internal/UpdateCheckService.cs @@ -27,6 +27,9 @@ internal sealed class UpdateCheckService : BackgroundService, IUpdateService private volatile UpdateStatus current; + /// + /// Initializes a new instance of the class. + /// public UpdateCheckService( UpdatesConfiguration configuration, IHttpClientFactory httpClientFactory, @@ -50,6 +53,9 @@ public UpdateCheckService( CheckedAt: null); } + /// + /// Gets the current. + /// public UpdateStatus Current => current; protected override async Task ExecuteAsync(CancellationToken cancellationToken) diff --git a/Proxytrace.Domain/Agent/IAgent.cs b/Proxytrace.Domain/Agent/IAgent.cs index 69e2de4d8..b2eff2ddd 100644 --- a/Proxytrace.Domain/Agent/IAgent.cs +++ b/Proxytrace.Domain/Agent/IAgent.cs @@ -38,6 +38,9 @@ public interface IAgent : Nordstein.Core.AI.Agents.IAgent, IDomainEntity SearchKind ISearchable.SearchKind => SearchKind.Agent; + /// + /// Factory delegate for creating a new new instance. + /// public delegate IAgent CreateNew( string name, IPromptTemplate systemPrompt, @@ -47,6 +50,9 @@ public delegate IAgent CreateNew( IModelParameters modelParameters, bool isSystemAgent = false); + /// + /// Factory delegate for creating a new existing instance. + /// public delegate IAgent CreateExisting( string name, IProject project, diff --git a/Proxytrace.Domain/Agent/IAgentGenerator.cs b/Proxytrace.Domain/Agent/IAgentGenerator.cs index 916a5ceb1..83358985a 100644 --- a/Proxytrace.Domain/Agent/IAgentGenerator.cs +++ b/Proxytrace.Domain/Agent/IAgentGenerator.cs @@ -1,5 +1,8 @@ namespace Proxytrace.Domain.Agent; +/// +/// Generates agent instances. +/// public interface IAgentGenerator : IDomainEntityGenerator { Task CreateAsync( diff --git a/Proxytrace.Domain/Agent/Internal/Agent.cs b/Proxytrace.Domain/Agent/Internal/Agent.cs index 2ead16558..5824d3404 100644 --- a/Proxytrace.Domain/Agent/Internal/Agent.cs +++ b/Proxytrace.Domain/Agent/Internal/Agent.cs @@ -25,10 +25,25 @@ internal record Agent : DomainEntity, IAgent private readonly Lazy agentRepository; private readonly IAsyncLock locker; + /// + /// Gets the name. + /// public string Name { get; } + /// + /// Gets or sets the endpoint. + /// public IModelEndpoint Endpoint { get; private init; } + /// + /// Gets the project. + /// public IProject Project { get; } + /// + /// Gets or sets the model parameters. + /// public IModelParameters ModelParameters { get; private init; } + /// + /// Gets the is system agent. + /// public bool IsSystemAgent { get; } private IAgentVersion? CurrentVersion { get; init; } @@ -38,9 +53,15 @@ IAgentVersion IAgent.CurrentVersion $"Agent {Id} ({Name}) has no current version. " + "This should only be observable inside the IAgent.CreateNew factory before WithInitialVersion is called."); + /// + /// Provides additional functionality. + /// public IPromptTemplate SystemPrompt => ((IAgent)this).CurrentVersion.SystemPrompt; + /// + /// Provides additional functionality. + /// public IReadOnlyList Tools => ((IAgent)this).CurrentVersion.Tools; @@ -117,11 +138,17 @@ public Agent( internal Agent WithInitialVersion(IAgentVersion version) => this with { CurrentVersion = version }; + /// + /// Creates the client. + /// public IModelClient CreateClient( IModelEndpoint? customEndpoint = null, bool skipIngestion = false) => modelClientFactory(this, customEndpoint, skipIngestion: skipIngestion); + /// + /// Creates the new version asynchronously. + /// public async Task CreateNewVersionAsync( IPromptTemplate systemPrompt, IReadOnlyList tools, @@ -140,6 +167,9 @@ public async Task CreateNewVersionAsync( return await ReloadAsync(cancellationToken); } + /// + /// Change system message. + /// public Task ChangeSystemMessage( IPromptTemplate systemPrompt, CancellationToken cancellationToken = default) @@ -147,6 +177,9 @@ public Task ChangeSystemMessage( ? Task.FromResult(this) : CreateNewVersionAsync(systemPrompt, Tools, cancellationToken); + /// + /// Change tools. + /// public Task ChangeTools( IReadOnlyList tools, CancellationToken cancellationToken = default) @@ -154,6 +187,9 @@ public Task ChangeTools( ? Task.FromResult(this) : CreateNewVersionAsync(SystemPrompt, tools, cancellationToken); + /// + /// Change endpoint. + /// public Task ChangeEndpoint(IModelEndpoint modelEndpoint, CancellationToken cancellationToken = default) { @@ -168,6 +204,9 @@ public Task ChangeEndpoint(IModelEndpoint modelEndpoint, return ApplyAsync(this with { Endpoint = modelEndpoint }, cancellationToken); } + /// + /// Change model parameters. + /// public Task ChangeModelParameters( IModelParameters modelParameters, CancellationToken cancellationToken = default) @@ -175,9 +214,15 @@ public Task ChangeModelParameters( ? Task.FromResult(this) : ApplyAsync(this with { ModelParameters = modelParameters }, cancellationToken); + /// + /// Creates the system message. + /// public SystemMessage CreateSystemMessage(IReadOnlyDictionary? variables = null) => Message.CreateSystemMessage(SystemPrompt, variables); + /// + /// Validates. + /// public override IEnumerable Validate(ValidationContext validationContext) { foreach (var result in base.Validate(validationContext)) diff --git a/Proxytrace.Domain/Agent/Internal/AgentGenerator.cs b/Proxytrace.Domain/Agent/Internal/AgentGenerator.cs index 209ec1740..61d0454c7 100644 --- a/Proxytrace.Domain/Agent/Internal/AgentGenerator.cs +++ b/Proxytrace.Domain/Agent/Internal/AgentGenerator.cs @@ -18,6 +18,9 @@ internal class AgentGenerator : DomainEntityGenerator, IAgentGenerator private readonly IPromptTemplate.Create createPrompt; private readonly IDomainObjectGenerator modelParametersGenerator; + /// + /// Initializes a new instance of the class. + /// public AgentGenerator( IAgent.CreateNew factory, IRepository repository, @@ -38,6 +41,9 @@ public AgentGenerator( this.modelParametersGenerator = modelParametersGenerator; } + /// + /// Generates asynchronously. + /// public override async Task GenerateAsync(CancellationToken cancellationToken = default) { var project = await projectGenerator.GetOrCreateAsync(cancellationToken); @@ -54,6 +60,9 @@ public override async Task GenerateAsync(CancellationToken cancellationT modelParameters: modelParameters); } + /// + /// Creates asynchronously. + /// public override async Task CreateAsync(CancellationToken cancellationToken = default) { var project = await projectGenerator.GetOrCreateAsync(cancellationToken); @@ -72,6 +81,9 @@ public override async Task CreateAsync(CancellationToken cancellationTok cancellationToken: cancellationToken); } + /// + /// Creates asynchronously. + /// public async Task CreateAsync( string name, string? systemPrompt = null, diff --git a/Proxytrace.Domain/AgentCall/AgentCallFilter.cs b/Proxytrace.Domain/AgentCall/AgentCallFilter.cs index bdd4851e4..5cf91ff6c 100644 --- a/Proxytrace.Domain/AgentCall/AgentCallFilter.cs +++ b/Proxytrace.Domain/AgentCall/AgentCallFilter.cs @@ -1,5 +1,8 @@ namespace Proxytrace.Domain.AgentCall; +/// +/// Represents a agent call filter. +/// public record AgentCallFilter( Guid? AgentId = null, Guid? ProjectId = null, diff --git a/Proxytrace.Domain/AgentCall/AgentCallHistogram.cs b/Proxytrace.Domain/AgentCall/AgentCallHistogram.cs index 88703c3c6..1e1300a79 100644 --- a/Proxytrace.Domain/AgentCall/AgentCallHistogram.cs +++ b/Proxytrace.Domain/AgentCall/AgentCallHistogram.cs @@ -1,5 +1,8 @@ namespace Proxytrace.Domain.AgentCall; +/// +/// Represents a agent call histogram bucket. +/// public record AgentCallHistogramBucket(DateTimeOffset Start, int Total, int Errors); /// @@ -11,6 +14,9 @@ public record AgentCallHistogramBucket(DateTimeOffset Start, int Total, int Erro /// public static class AgentCallHistogram { + /// + /// The error status threshold constant value. + /// public const int ErrorStatusThreshold = 400; /// diff --git a/Proxytrace.Domain/AgentCall/IAgentCall.cs b/Proxytrace.Domain/AgentCall/IAgentCall.cs index 1880b30ba..fe456cdf7 100644 --- a/Proxytrace.Domain/AgentCall/IAgentCall.cs +++ b/Proxytrace.Domain/AgentCall/IAgentCall.cs @@ -76,6 +76,9 @@ public interface IAgentCall : IDomainEntity, ISearchable SearchKind ISearchable.SearchKind => SearchKind.AgentCall; + /// + /// Factory delegate for creating a new new instance. + /// public delegate IAgentCall CreateNew( IAgent agent, IAgentVersion version, @@ -91,6 +94,9 @@ public delegate IAgentCall CreateNew( OutlierFlags outlierFlags = OutlierFlags.None, Guid? apiKeyId = null); + /// + /// Factory delegate for creating a new existing instance. + /// public delegate IAgentCall CreateExisting( IAgent agent, IAgentVersion version, diff --git a/Proxytrace.Domain/AgentCall/IAgentCallGenerator.cs b/Proxytrace.Domain/AgentCall/IAgentCallGenerator.cs index 460012acb..9432b858f 100644 --- a/Proxytrace.Domain/AgentCall/IAgentCallGenerator.cs +++ b/Proxytrace.Domain/AgentCall/IAgentCallGenerator.cs @@ -1,5 +1,8 @@ namespace Proxytrace.Domain.AgentCall; +/// +/// Generates agent call instances. +/// public interface IAgentCallGenerator : IDomainEntityGenerator { Task CreateAsync(DateTimeOffset createdAt, CancellationToken cancellationToken = default); diff --git a/Proxytrace.Domain/AgentCall/Internal/AgentCall.cs b/Proxytrace.Domain/AgentCall/Internal/AgentCall.cs index 3d42652ea..8d438ed5a 100644 --- a/Proxytrace.Domain/AgentCall/Internal/AgentCall.cs +++ b/Proxytrace.Domain/AgentCall/Internal/AgentCall.cs @@ -12,21 +12,66 @@ namespace Proxytrace.Domain.AgentCall.Internal; internal record AgentCall : DomainEntity, IAgentCall { + /// + /// Gets the agent. + /// public IAgent Agent { get; } + /// + /// Gets the version. + /// public IAgentVersion Version { get; } + /// + /// Gets the endpoint. + /// public IModelEndpoint Endpoint { get; } + /// + /// Gets the request. + /// public Conversation Request { get; } + /// + /// Gets the response. + /// public ICompletion? Response { get; } + /// + /// Gets the http status. + /// public HttpStatusCode HttpStatus { get; } + /// + /// Gets the finish reason. + /// public string? FinishReason { get; } + /// + /// Gets the error message. + /// public string? ErrorMessage { get; } + /// + /// Gets the model parameters. + /// public IModelParameters ModelParameters { get; } + /// + /// Gets the conversation id. + /// public Guid? ConversationId { get; } + /// + /// Gets the session id. + /// public Guid? SessionId { get; } + /// + /// Gets the outlier flags. + /// public OutlierFlags OutlierFlags { get; } + /// + /// Gets the api key id. + /// public Guid? ApiKeyId { get; } + /// + /// Gets the project. + /// public IProject Project => Agent.Project; + /// + /// Initializes a new instance of the class. + /// public AgentCall( IAgent agent, IAgentVersion version, @@ -58,6 +103,9 @@ public AgentCall( ApiKeyId = apiKeyId; } + /// + /// Initializes a new instance of the class. + /// public AgentCall( IAgent agent, IAgentVersion version, @@ -90,6 +138,9 @@ public AgentCall( ApiKeyId = apiKeyId; } + /// + /// Validates. + /// public override IEnumerable Validate(ValidationContext validationContext) { foreach (var result in base.Validate(validationContext)) diff --git a/Proxytrace.Domain/AgentCall/Internal/AgentCallGenerator.cs b/Proxytrace.Domain/AgentCall/Internal/AgentCallGenerator.cs index 60b74263c..aebcf1839 100644 --- a/Proxytrace.Domain/AgentCall/Internal/AgentCallGenerator.cs +++ b/Proxytrace.Domain/AgentCall/Internal/AgentCallGenerator.cs @@ -19,6 +19,9 @@ internal class AgentCallGenerator : DomainEntityGenerator, IAgentCal private readonly IDomainObjectGenerator conversationGenerator; private readonly IDomainObjectGenerator completionGenerator; + /// + /// Initializes a new instance of the class. + /// public AgentCallGenerator( IAgentCall.CreateNew factory, IAgentCall.CreateExisting createExisting, @@ -39,6 +42,9 @@ public AgentCallGenerator( this.completionGenerator = completionGenerator; } + /// + /// Generates asynchronously. + /// public override async Task GenerateAsync(CancellationToken cancellationToken = default) { var agent = await agentGenerator.CreateAsync(cancellationToken); @@ -56,6 +62,9 @@ public override async Task GenerateAsync(CancellationToken cancellat conversationId: null); } + /// + /// Creates asynchronously. + /// public async Task CreateAsync(DateTimeOffset createdAt, CancellationToken cancellationToken = default) { var agentCall = await CreateAsync(cancellationToken); diff --git a/Proxytrace.Domain/AgentVersion/IAgentVersion.cs b/Proxytrace.Domain/AgentVersion/IAgentVersion.cs index 03f5a07ce..21b111162 100644 --- a/Proxytrace.Domain/AgentVersion/IAgentVersion.cs +++ b/Proxytrace.Domain/AgentVersion/IAgentVersion.cs @@ -37,6 +37,9 @@ public interface IAgentVersion : IDomainEntity /// Task MoveToAgentAsync(IAgent targetAgent, CancellationToken cancellationToken = default); + /// + /// Factory delegate for creating a new new instance. + /// public delegate IAgentVersion CreateNew( Guid projectId, Guid agentId, @@ -44,6 +47,9 @@ public delegate IAgentVersion CreateNew( IPromptTemplate systemPrompt, IReadOnlyList tools); + /// + /// Factory delegate for creating a new existing instance. + /// public delegate IAgentVersion CreateExisting( Guid projectId, Guid agentId, diff --git a/Proxytrace.Domain/AgentVersion/IAgentVersionGenerator.cs b/Proxytrace.Domain/AgentVersion/IAgentVersionGenerator.cs index 8b79a31fc..b1718e409 100644 --- a/Proxytrace.Domain/AgentVersion/IAgentVersionGenerator.cs +++ b/Proxytrace.Domain/AgentVersion/IAgentVersionGenerator.cs @@ -1,3 +1,6 @@ namespace Proxytrace.Domain.AgentVersion; +/// +/// Generates agent version instances. +/// public interface IAgentVersionGenerator : IDomainEntityGenerator; diff --git a/Proxytrace.Domain/AgentVersion/Internal/AgentVersion.cs b/Proxytrace.Domain/AgentVersion/Internal/AgentVersion.cs index 8b6f0afcd..a58288a3c 100644 --- a/Proxytrace.Domain/AgentVersion/Internal/AgentVersion.cs +++ b/Proxytrace.Domain/AgentVersion/Internal/AgentVersion.cs @@ -15,12 +15,30 @@ internal record AgentVersion : DomainEntity, IAgentVersion private readonly Lazy versionRepository; private readonly IAsyncLock locker; + /// + /// Gets or sets the project id. + /// public Guid ProjectId { get; private init; } + /// + /// Gets or sets the agent id. + /// public Guid AgentId { get; private init; } + /// + /// Gets or sets the version number. + /// public int VersionNumber { get; private init; } + /// + /// Gets the system prompt. + /// public IPromptTemplate SystemPrompt { get; } + /// + /// Gets the tools. + /// public IReadOnlyList Tools { get; } + /// + /// Initializes a new instance of the class. + /// public AgentVersion( Guid projectId, Guid agentId, @@ -42,6 +60,9 @@ public AgentVersion( Tools = tools; } + /// + /// Initializes a new instance of the class. + /// public AgentVersion( Guid projectId, Guid agentId, @@ -64,9 +85,15 @@ public AgentVersion( Tools = tools; } + /// + /// Gets the agent asynchronously. + /// public Task GetAgentAsync(CancellationToken cancellationToken = default) => agentRepository.GetAsync(AgentId, cancellationToken); + /// + /// Moves the to agent asynchronously. + /// public async Task MoveToAgentAsync(IAgent targetAgent, CancellationToken cancellationToken = default) { // Process-local lock — same caveat as Agent.CreateNewVersionAsync: in multi-replica @@ -83,6 +110,9 @@ public async Task MoveToAgentAsync(IAgent targetAgent, Cancellati cancellationToken); } + /// + /// Validates. + /// public override IEnumerable Validate(ValidationContext validationContext) { foreach (var result in base.Validate(validationContext)) diff --git a/Proxytrace.Domain/AgentVersion/Internal/AgentVersionFingerprinter.cs b/Proxytrace.Domain/AgentVersion/Internal/AgentVersionFingerprinter.cs index fcb58822a..757fc1d63 100644 --- a/Proxytrace.Domain/AgentVersion/Internal/AgentVersionFingerprinter.cs +++ b/Proxytrace.Domain/AgentVersion/Internal/AgentVersionFingerprinter.cs @@ -14,6 +14,9 @@ internal sealed class AgentVersionFingerprinter : IAgentVersionFingerprinter // parse+rewrite round-trip on hot ingestion paths. Bounded by the number of unique tool schemas // in the project, which is small in practice. private readonly ConcurrentDictionary strippedSchemaCache = new(); + /// + /// Strict. + /// public string Strict(IPromptTemplate systemPrompt, IReadOnlyCollection tools) { var sb = new StringBuilder(); @@ -27,6 +30,9 @@ public string Strict(IPromptTemplate systemPrompt, IReadOnlyCollection + /// Loose. + /// public string Loose(IPromptTemplate systemPrompt, IReadOnlyCollection tools) { var sb = new StringBuilder(); diff --git a/Proxytrace.Domain/AgentVersion/Internal/AgentVersionGenerator.cs b/Proxytrace.Domain/AgentVersion/Internal/AgentVersionGenerator.cs index 6e2edc9d4..41ef51f59 100644 --- a/Proxytrace.Domain/AgentVersion/Internal/AgentVersionGenerator.cs +++ b/Proxytrace.Domain/AgentVersion/Internal/AgentVersionGenerator.cs @@ -13,6 +13,9 @@ internal class AgentVersionGenerator : DomainEntityGenerator, IAg private readonly IAgentVersionRepository versionRepository; private readonly IDomainObjectGenerator promptTemplateGenerator; + /// + /// Initializes a new instance of the class. + /// public AgentVersionGenerator( IAgentVersion.CreateNew factory, IRepository repository, @@ -27,6 +30,9 @@ public AgentVersionGenerator( this.promptTemplateGenerator = promptTemplateGenerator; } + /// + /// Generates asynchronously. + /// public override async Task GenerateAsync(CancellationToken cancellationToken = default) { var agent = await agentGenerator.GetOrCreateAsync(cancellationToken); diff --git a/Proxytrace.Domain/ApiKey/Internal/ApiKey.cs b/Proxytrace.Domain/ApiKey/Internal/ApiKey.cs index cd04839b6..2f53ad88b 100644 --- a/Proxytrace.Domain/ApiKey/Internal/ApiKey.cs +++ b/Proxytrace.Domain/ApiKey/Internal/ApiKey.cs @@ -9,14 +9,38 @@ namespace Proxytrace.Domain.ApiKey.Internal; internal record ApiKey : DomainEntity, IApiKey { + /// + /// Gets the name. + /// public string Name { get; } + /// + /// Gets the key hash. + /// public string KeyHash { get; } + /// + /// Gets the key prefix. + /// public string KeyPrefix { get; } + /// + /// Gets the project. + /// public IProject Project { get; } + /// + /// Gets the provider. + /// public IModelProvider Provider { get; } + /// + /// Gets the scopes. + /// public ApiKeyScopes Scopes { get; } + /// + /// Gets the owner. + /// public IUser Owner { get; } + /// + /// Initializes a new instance of the class. + /// public ApiKey( string name, string keyHash, @@ -36,6 +60,9 @@ public ApiKey( Owner = owner; } + /// + /// Initializes a new instance of the class. + /// public ApiKey( string name, string keyHash, @@ -56,6 +83,9 @@ public ApiKey( Owner = owner; } + /// + /// Validates. + /// public override IEnumerable Validate(ValidationContext validationContext) { foreach (var result in base.Validate(validationContext)) diff --git a/Proxytrace.Domain/ApiKey/Internal/ApiKeyGenerator.cs b/Proxytrace.Domain/ApiKey/Internal/ApiKeyGenerator.cs index 1481bfa7d..802bf1efa 100644 --- a/Proxytrace.Domain/ApiKey/Internal/ApiKeyGenerator.cs +++ b/Proxytrace.Domain/ApiKey/Internal/ApiKeyGenerator.cs @@ -14,6 +14,9 @@ internal class ApiKeyGenerator : DomainEntityGenerator private readonly IDomainEntityGenerator providerGenerator; private readonly IDomainEntityGenerator userGenerator; + /// + /// Initializes a new instance of the class. + /// public ApiKeyGenerator( IApiKey.CreateNew factory, IRepository repository, @@ -28,6 +31,9 @@ public ApiKeyGenerator( this.userGenerator = userGenerator; } + /// + /// Generates asynchronously. + /// public override async Task GenerateAsync(CancellationToken cancellationToken = default) { var project = await projectGenerator.GetOrCreateAsync(cancellationToken); diff --git a/Proxytrace.Domain/ApplicationError/IApplicationError.cs b/Proxytrace.Domain/ApplicationError/IApplicationError.cs index 12d526ff2..8e49bf412 100644 --- a/Proxytrace.Domain/ApplicationError/IApplicationError.cs +++ b/Proxytrace.Domain/ApplicationError/IApplicationError.cs @@ -21,6 +21,9 @@ public interface IApplicationError : IDomainEntity /// The full exception stacktrace, or when no exception was logged. string? StackTrace { get; } + /// + /// Factory delegate for creating a new new instance. + /// public delegate IApplicationError CreateNew( string message, ApplicationErrorLevel level, @@ -28,6 +31,9 @@ public delegate IApplicationError CreateNew( string? exceptionType, string? stackTrace); + /// + /// Factory delegate for creating a new existing instance. + /// public delegate IApplicationError CreateExisting( string message, ApplicationErrorLevel level, diff --git a/Proxytrace.Domain/ApplicationError/Internal/ApplicationError.cs b/Proxytrace.Domain/ApplicationError/Internal/ApplicationError.cs index c9262a6e9..fd371622a 100644 --- a/Proxytrace.Domain/ApplicationError/Internal/ApplicationError.cs +++ b/Proxytrace.Domain/ApplicationError/Internal/ApplicationError.cs @@ -6,12 +6,30 @@ namespace Proxytrace.Domain.ApplicationError.Internal; internal record ApplicationError : DomainEntity, IApplicationError { + /// + /// Gets the message. + /// public string Message { get; } + /// + /// Gets the level. + /// public ApplicationErrorLevel Level { get; } + /// + /// Gets the category. + /// public string Category { get; } + /// + /// Gets the exception type. + /// public string? ExceptionType { get; } + /// + /// Gets the stack trace. + /// public string? StackTrace { get; } + /// + /// Initializes a new instance of the class. + /// public ApplicationError( string message, ApplicationErrorLevel level, @@ -27,6 +45,9 @@ public ApplicationError( StackTrace = stackTrace; } + /// + /// Initializes a new instance of the class. + /// public ApplicationError( string message, ApplicationErrorLevel level, @@ -43,6 +64,9 @@ public ApplicationError( StackTrace = stackTrace; } + /// + /// Validates. + /// public override IEnumerable Validate(ValidationContext validationContext) { foreach (var result in base.Validate(validationContext)) diff --git a/Proxytrace.Domain/ApplicationError/Internal/ApplicationErrorGenerator.cs b/Proxytrace.Domain/ApplicationError/Internal/ApplicationErrorGenerator.cs index 656652e86..f580e9aef 100644 --- a/Proxytrace.Domain/ApplicationError/Internal/ApplicationErrorGenerator.cs +++ b/Proxytrace.Domain/ApplicationError/Internal/ApplicationErrorGenerator.cs @@ -9,6 +9,9 @@ internal class ApplicationErrorGenerator : DomainEntityGenerator + /// Initializes a new instance of the class. + /// public ApplicationErrorGenerator( IApplicationError.CreateNew factory, IApplicationError.CreateExisting createExisting, @@ -19,6 +22,9 @@ public ApplicationErrorGenerator( this.createExisting = createExisting; } + /// + /// Generates asynchronously. + /// public override Task GenerateAsync(CancellationToken cancellationToken = default) => factory( message: $"Something failed: {random.UniqueString()}", @@ -28,6 +34,9 @@ public override Task GenerateAsync(CancellationToken cancella stackTrace: $" at Proxytrace.Test.{random.UniqueString()}()") .ToTaskResult(); + /// + /// Creates asynchronously. + /// public async Task CreateAsync(DateTimeOffset createdAt, CancellationToken cancellationToken = default) { var error = await CreateAsync(cancellationToken); diff --git a/Proxytrace.Domain/AuditLog/AuditState.cs b/Proxytrace.Domain/AuditLog/AuditState.cs index d27ebfa7d..e35964780 100644 --- a/Proxytrace.Domain/AuditLog/AuditState.cs +++ b/Proxytrace.Domain/AuditLog/AuditState.cs @@ -12,14 +12,38 @@ namespace Proxytrace.Domain.AuditLog; /// public readonly struct AuditState : IReadOnlyList> { + /// + /// Gets the action. + /// public AuditAction Action { get; } + /// + /// Gets the target type. + /// public string TargetType { get; } + /// + /// Gets the target id. + /// public Guid? TargetId { get; } + /// + /// Gets the target label. + /// public string? TargetLabel { get; } + /// + /// Gets the project id. + /// public Guid? ProjectId { get; } + /// + /// Gets the details. + /// public string? Details { get; } + /// + /// Gets the outcome. + /// public AuditOutcome Outcome { get; } + /// + /// Initializes a new instance of the class. + /// public AuditState( AuditAction action, string targetType, @@ -38,13 +62,22 @@ public AuditState( Outcome = outcome; } + /// + /// Formats. + /// public static string Format(AuditState state, Exception? _) => $"Audit {state.Action}: {state.TargetType}" + (state.TargetId is { } id ? $" {id}" : string.Empty) + (state.TargetLabel is { Length: > 0 } label ? $" ({label})" : string.Empty); + /// + /// Gets the count. + /// public int Count => 7; + /// + /// Gets the element at the specified index. + /// public KeyValuePair this[int index] => index switch { 0 => new KeyValuePair("Action", Action), @@ -57,6 +90,9 @@ public static string Format(AuditState state, Exception? _) _ => throw new ArgumentOutOfRangeException(nameof(index)), }; + /// + /// Returns an enumerator that iterates through the collection. + /// public IEnumerator> GetEnumerator() { for (var i = 0; i < Count; i++) diff --git a/Proxytrace.Domain/AuditLog/IAuditLogEntry.cs b/Proxytrace.Domain/AuditLog/IAuditLogEntry.cs index 1a169215e..7638de263 100644 --- a/Proxytrace.Domain/AuditLog/IAuditLogEntry.cs +++ b/Proxytrace.Domain/AuditLog/IAuditLogEntry.cs @@ -42,6 +42,9 @@ public interface IAuditLogEntry : IDomainEntity /// Whether the action succeeded. AuditOutcome Outcome { get; } + /// + /// Factory delegate for creating a new new instance. + /// public delegate IAuditLogEntry CreateNew( AuditAction action, AuditActorType actorType, @@ -55,6 +58,9 @@ public delegate IAuditLogEntry CreateNew( string? details, AuditOutcome outcome); + /// + /// Factory delegate for creating a new existing instance. + /// public delegate IAuditLogEntry CreateExisting( AuditAction action, AuditActorType actorType, diff --git a/Proxytrace.Domain/AuditLog/Internal/AuditLogEntry.cs b/Proxytrace.Domain/AuditLog/Internal/AuditLogEntry.cs index 18e7adc73..f294c2540 100644 --- a/Proxytrace.Domain/AuditLog/Internal/AuditLogEntry.cs +++ b/Proxytrace.Domain/AuditLog/Internal/AuditLogEntry.cs @@ -6,18 +6,54 @@ namespace Proxytrace.Domain.AuditLog.Internal; internal record AuditLogEntry : DomainEntity, IAuditLogEntry { + /// + /// Gets the action. + /// public AuditAction Action { get; } + /// + /// Gets the actor type. + /// public AuditActorType ActorType { get; } + /// + /// Gets the actor user id. + /// public Guid? ActorUserId { get; } + /// + /// Gets the actor email. + /// public string? ActorEmail { get; } + /// + /// Gets the actor api key id. + /// public Guid? ActorApiKeyId { get; } + /// + /// Gets the project id. + /// public Guid? ProjectId { get; } + /// + /// Gets the target type. + /// public string TargetType { get; } + /// + /// Gets the target id. + /// public Guid? TargetId { get; } + /// + /// Gets the target label. + /// public string? TargetLabel { get; } + /// + /// Gets the details. + /// public string? Details { get; } + /// + /// Gets the outcome. + /// public AuditOutcome Outcome { get; } + /// + /// Initializes a new instance of the class. + /// public AuditLogEntry( AuditAction action, AuditActorType actorType, @@ -45,6 +81,9 @@ public AuditLogEntry( Outcome = outcome; } + /// + /// Initializes a new instance of the class. + /// public AuditLogEntry( AuditAction action, AuditActorType actorType, @@ -73,6 +112,9 @@ public AuditLogEntry( Outcome = outcome; } + /// + /// Validates. + /// public override IEnumerable Validate(ValidationContext validationContext) { foreach (var result in base.Validate(validationContext)) diff --git a/Proxytrace.Domain/AuditLog/Internal/AuditLogEntryGenerator.cs b/Proxytrace.Domain/AuditLog/Internal/AuditLogEntryGenerator.cs index f486f2d02..ef6da9557 100644 --- a/Proxytrace.Domain/AuditLog/Internal/AuditLogEntryGenerator.cs +++ b/Proxytrace.Domain/AuditLog/Internal/AuditLogEntryGenerator.cs @@ -9,6 +9,9 @@ internal class AuditLogEntryGenerator : DomainEntityGenerator, I private readonly IAuditLogEntry.CreateNew factory; private readonly IAuditLogEntry.CreateExisting createExisting; + /// + /// Initializes a new instance of the class. + /// public AuditLogEntryGenerator( IAuditLogEntry.CreateNew factory, IAuditLogEntry.CreateExisting createExisting, @@ -19,6 +22,9 @@ public AuditLogEntryGenerator( this.createExisting = createExisting; } + /// + /// Generates asynchronously. + /// public override Task GenerateAsync(CancellationToken cancellationToken = default) => factory( action: random.Enum(), @@ -34,6 +40,9 @@ public override Task GenerateAsync(CancellationToken cancellatio outcome: AuditOutcome.Success) .ToTaskResult(); + /// + /// Creates asynchronously. + /// public async Task CreateAsync(DateTimeOffset createdAt, CancellationToken cancellationToken = default) { var entry = await CreateAsync(cancellationToken); diff --git a/Proxytrace.Domain/CostLimit/ICostLimit.cs b/Proxytrace.Domain/CostLimit/ICostLimit.cs index d55c7210c..d1abb0fe6 100644 --- a/Proxytrace.Domain/CostLimit/ICostLimit.cs +++ b/Proxytrace.Domain/CostLimit/ICostLimit.cs @@ -53,6 +53,9 @@ public interface ICostLimit : IDomainEntity /// Whether the guard evaluates this limit and the proxy enforces its hard threshold. bool Enabled { get; } + /// + /// Factory delegate for creating a new new instance. + /// public delegate ICostLimit CreateNew( IProject project, IAgent? agent, @@ -61,6 +64,9 @@ public delegate ICostLimit CreateNew( decimal? hardLimitEur, bool enabled); + /// + /// Factory delegate for creating a new existing instance. + /// public delegate ICostLimit CreateExisting( IProject project, IAgent? agent, diff --git a/Proxytrace.Domain/CostLimit/ICostLimitRepository.cs b/Proxytrace.Domain/CostLimit/ICostLimitRepository.cs index 5e6036e91..80b34c2b0 100644 --- a/Proxytrace.Domain/CostLimit/ICostLimitRepository.cs +++ b/Proxytrace.Domain/CostLimit/ICostLimitRepository.cs @@ -1,5 +1,8 @@ namespace Proxytrace.Domain.CostLimit; +/// +/// Repository for persisting and querying cost limit entities. +/// public interface ICostLimitRepository : IRepository { /// The project's limits — the project-wide one plus every agent override. diff --git a/Proxytrace.Domain/CostLimit/Internal/CostLimit.cs b/Proxytrace.Domain/CostLimit/Internal/CostLimit.cs index b03d5d4b4..914eae22b 100644 --- a/Proxytrace.Domain/CostLimit/Internal/CostLimit.cs +++ b/Proxytrace.Domain/CostLimit/Internal/CostLimit.cs @@ -9,13 +9,34 @@ namespace Proxytrace.Domain.CostLimit.Internal; internal record CostLimit : DomainEntity, ICostLimit { + /// + /// Gets or sets the project. + /// public IProject Project { get; private init; } + /// + /// Gets or sets the agent. + /// public IAgent? Agent { get; private init; } + /// + /// Gets or sets the api key. + /// public IApiKey? ApiKey { get; private init; } + /// + /// Gets or sets the soft limit eur. + /// public decimal? SoftLimitEur { get; private init; } + /// + /// Gets or sets the hard limit eur. + /// public decimal? HardLimitEur { get; private init; } + /// + /// Gets or sets the enabled. + /// public bool Enabled { get; private init; } + /// + /// Initializes a new instance of the class. + /// public CostLimit( IProject project, IAgent? agent, @@ -33,6 +54,9 @@ public CostLimit( Enabled = enabled; } + /// + /// Initializes a new instance of the class. + /// public CostLimit( IProject project, IAgent? agent, @@ -51,6 +75,9 @@ public CostLimit( Enabled = enabled; } + /// + /// Updates. + /// public Task Update( decimal? softLimitEur, decimal? hardLimitEur, @@ -63,6 +90,9 @@ public Task Update( Enabled = enabled, }, cancellationToken); + /// + /// Validates. + /// public override IEnumerable Validate(ValidationContext validationContext) { foreach (var result in base.Validate(validationContext)) diff --git a/Proxytrace.Domain/CostLimit/Internal/CostLimitGenerator.cs b/Proxytrace.Domain/CostLimit/Internal/CostLimitGenerator.cs index c75fead7e..505ec0bed 100644 --- a/Proxytrace.Domain/CostLimit/Internal/CostLimitGenerator.cs +++ b/Proxytrace.Domain/CostLimit/Internal/CostLimitGenerator.cs @@ -9,6 +9,9 @@ internal class CostLimitGenerator : DomainEntityGenerator private readonly ICostLimit.CreateNew factory; private readonly IDomainEntityGenerator projectGenerator; + /// + /// Initializes a new instance of the class. + /// public CostLimitGenerator( ICostLimit.CreateNew factory, IRepository repository, @@ -19,6 +22,9 @@ public CostLimitGenerator( this.projectGenerator = projectGenerator; } + /// + /// Generates asynchronously. + /// public override async Task GenerateAsync(CancellationToken cancellationToken = default) { IProject project = await projectGenerator.GetOrCreateAsync(cancellationToken); diff --git a/Proxytrace.Domain/CostLimitBreach/ICostLimitBreach.cs b/Proxytrace.Domain/CostLimitBreach/ICostLimitBreach.cs index eabc2e948..73c9fe091 100644 --- a/Proxytrace.Domain/CostLimitBreach/ICostLimitBreach.cs +++ b/Proxytrace.Domain/CostLimitBreach/ICostLimitBreach.cs @@ -29,12 +29,18 @@ public interface ICostLimitBreach : IDomainEntity /// The month-to-date spend in EUR measured at the moment of the crossing. decimal SpendEur { get; } + /// + /// Factory delegate for creating a new new instance. + /// public delegate ICostLimitBreach CreateNew( ICostLimit costLimit, DateTimeOffset monthStart, CostThreshold threshold, decimal spendEur); + /// + /// Factory delegate for creating a new existing instance. + /// public delegate ICostLimitBreach CreateExisting( ICostLimit costLimit, DateTimeOffset monthStart, diff --git a/Proxytrace.Domain/CostLimitBreach/ICostLimitBreachRepository.cs b/Proxytrace.Domain/CostLimitBreach/ICostLimitBreachRepository.cs index 29335401e..dd33bcb99 100644 --- a/Proxytrace.Domain/CostLimitBreach/ICostLimitBreachRepository.cs +++ b/Proxytrace.Domain/CostLimitBreach/ICostLimitBreachRepository.cs @@ -35,6 +35,9 @@ public sealed record BudgetHardBlock( /// public sealed record FiredThreshold(Guid CostLimitId, CostThreshold Threshold); +/// +/// Repository for persisting and querying cost limit breach entities. +/// public interface ICostLimitBreachRepository : IRepository { /// diff --git a/Proxytrace.Domain/CostLimitBreach/Internal/CostLimitBreach.cs b/Proxytrace.Domain/CostLimitBreach/Internal/CostLimitBreach.cs index 2e5c5916e..fca70c77a 100644 --- a/Proxytrace.Domain/CostLimitBreach/Internal/CostLimitBreach.cs +++ b/Proxytrace.Domain/CostLimitBreach/Internal/CostLimitBreach.cs @@ -7,11 +7,26 @@ namespace Proxytrace.Domain.CostLimitBreach.Internal; internal record CostLimitBreach : DomainEntity, ICostLimitBreach { + /// + /// Gets or sets the cost limit. + /// public ICostLimit CostLimit { get; private init; } + /// + /// Gets or sets the month start. + /// public DateTimeOffset MonthStart { get; private init; } + /// + /// Gets or sets the threshold. + /// public CostThreshold Threshold { get; private init; } + /// + /// Gets or sets the spend eur. + /// public decimal SpendEur { get; private init; } + /// + /// Initializes a new instance of the class. + /// public CostLimitBreach( ICostLimit costLimit, DateTimeOffset monthStart, @@ -25,6 +40,9 @@ public CostLimitBreach( SpendEur = spendEur; } + /// + /// Initializes a new instance of the class. + /// public CostLimitBreach( ICostLimit costLimit, DateTimeOffset monthStart, @@ -39,6 +57,9 @@ public CostLimitBreach( SpendEur = spendEur; } + /// + /// Validates. + /// public override IEnumerable Validate(ValidationContext validationContext) { foreach (var result in base.Validate(validationContext)) diff --git a/Proxytrace.Domain/CostLimitBreach/Internal/CostLimitBreachGenerator.cs b/Proxytrace.Domain/CostLimitBreach/Internal/CostLimitBreachGenerator.cs index 9f1f697b2..720c24346 100644 --- a/Proxytrace.Domain/CostLimitBreach/Internal/CostLimitBreachGenerator.cs +++ b/Proxytrace.Domain/CostLimitBreach/Internal/CostLimitBreachGenerator.cs @@ -9,6 +9,9 @@ internal class CostLimitBreachGenerator : DomainEntityGenerator costLimitGenerator; + /// + /// Initializes a new instance of the class. + /// public CostLimitBreachGenerator( ICostLimitBreach.CreateNew factory, IRepository repository, @@ -19,6 +22,9 @@ public CostLimitBreachGenerator( this.costLimitGenerator = costLimitGenerator; } + /// + /// Generates asynchronously. + /// public override async Task GenerateAsync(CancellationToken cancellationToken = default) { ICostLimit costLimit = await costLimitGenerator.GetOrCreateAsync(cancellationToken); diff --git a/Proxytrace.Domain/CustomAnomaly/ICustomAnomalyDetector.cs b/Proxytrace.Domain/CustomAnomaly/ICustomAnomalyDetector.cs index 79742289b..0b13c8f9a 100644 --- a/Proxytrace.Domain/CustomAnomaly/ICustomAnomalyDetector.cs +++ b/Proxytrace.Domain/CustomAnomaly/ICustomAnomalyDetector.cs @@ -12,6 +12,9 @@ namespace Proxytrace.Domain.CustomAnomaly; /// public interface ICustomAnomalyDetector : IDomainEntity { + /// + /// The max triggers constant value. + /// public const int MaxTriggers = 20; string Name { get; } @@ -43,6 +46,9 @@ public interface ICustomAnomalyDetector : IDomainEntity /// The owning project (the hidden agent's project). IProject Project { get; } + /// + /// Factory delegate for creating a new new instance. + /// public delegate ICustomAnomalyDetector CreateNew( string name, IAgent agent, @@ -52,6 +58,9 @@ public delegate ICustomAnomalyDetector CreateNew( bool isEnabled, bool blockUpstream); + /// + /// Factory delegate for creating a new existing instance. + /// public delegate ICustomAnomalyDetector CreateExisting( string name, IAgent agent, diff --git a/Proxytrace.Domain/CustomAnomaly/ICustomAnomalyDetectorRepository.cs b/Proxytrace.Domain/CustomAnomaly/ICustomAnomalyDetectorRepository.cs index 36fd85743..93f9d012f 100644 --- a/Proxytrace.Domain/CustomAnomaly/ICustomAnomalyDetectorRepository.cs +++ b/Proxytrace.Domain/CustomAnomaly/ICustomAnomalyDetectorRepository.cs @@ -14,6 +14,9 @@ public sealed record BlockingDetectorRule( bool AllAgents, IReadOnlyCollection ScopedAgentNames); +/// +/// Repository for persisting and querying custom anomaly detector entities. +/// public interface ICustomAnomalyDetectorRepository : IRepository { Task> GetByProjectAsync( diff --git a/Proxytrace.Domain/CustomAnomaly/ICustomAnomalyResult.cs b/Proxytrace.Domain/CustomAnomaly/ICustomAnomalyResult.cs index 527a957e9..6b3ad07c7 100644 --- a/Proxytrace.Domain/CustomAnomaly/ICustomAnomalyResult.cs +++ b/Proxytrace.Domain/CustomAnomaly/ICustomAnomalyResult.cs @@ -18,6 +18,9 @@ public interface ICustomAnomalyResult : IDomainEntity /// The judge's reasoning for the anomalous verdict, if it provided one. string? Reasoning { get; } + /// + /// Factory delegate for creating a new new instance. + /// public delegate ICustomAnomalyResult CreateNew( Guid detectorId, Guid agentCallId, @@ -25,6 +28,9 @@ public delegate ICustomAnomalyResult CreateNew( string matchedTrigger, string? reasoning); + /// + /// Factory delegate for creating a new existing instance. + /// public delegate ICustomAnomalyResult CreateExisting( Guid detectorId, Guid agentCallId, diff --git a/Proxytrace.Domain/CustomAnomaly/ICustomAnomalyResultRepository.cs b/Proxytrace.Domain/CustomAnomaly/ICustomAnomalyResultRepository.cs index f2b49f65b..3bdb15ae0 100644 --- a/Proxytrace.Domain/CustomAnomaly/ICustomAnomalyResultRepository.cs +++ b/Proxytrace.Domain/CustomAnomaly/ICustomAnomalyResultRepository.cs @@ -1,5 +1,8 @@ namespace Proxytrace.Domain.CustomAnomaly; +/// +/// Repository for persisting and querying custom anomaly result entities. +/// public interface ICustomAnomalyResultRepository : IRepository { /// diff --git a/Proxytrace.Domain/CustomAnomaly/Internal/CustomAnomalyDetector.cs b/Proxytrace.Domain/CustomAnomaly/Internal/CustomAnomalyDetector.cs index 69b584b5a..204813d44 100644 --- a/Proxytrace.Domain/CustomAnomaly/Internal/CustomAnomalyDetector.cs +++ b/Proxytrace.Domain/CustomAnomaly/Internal/CustomAnomalyDetector.cs @@ -9,17 +9,44 @@ namespace Proxytrace.Domain.CustomAnomaly.Internal; internal record CustomAnomalyDetector : DomainEntity, ICustomAnomalyDetector { + /// + /// Gets or sets the name. + /// public string Name { get; private init; } + /// + /// Gets or sets the agent. + /// public IAgent Agent { get; private init; } + /// + /// Gets or sets the triggers. + /// public IReadOnlyList Triggers { get; private init; } + /// + /// Gets or sets the all agents. + /// public bool AllAgents { get; private init; } + /// + /// Gets or sets the scoped agents. + /// public IReadOnlyCollection ScopedAgents { get; private init; } + /// + /// Gets or sets the is enabled. + /// public bool IsEnabled { get; private init; } + /// + /// Gets or sets the block upstream. + /// public bool BlockUpstream { get; private init; } + /// + /// Provides additional functionality. + /// public IProject Project => Agent.Project; + /// + /// Initializes a new instance of the class. + /// public CustomAnomalyDetector( string name, IAgent agent, @@ -39,6 +66,9 @@ public CustomAnomalyDetector( BlockUpstream = blockUpstream; } + /// + /// Initializes a new instance of the class. + /// public CustomAnomalyDetector( string name, IAgent agent, @@ -59,6 +89,9 @@ public CustomAnomalyDetector( BlockUpstream = blockUpstream; } + /// + /// Updates. + /// public Task Update( string name, IReadOnlyList triggers, @@ -77,6 +110,9 @@ public Task Update( BlockUpstream = blockUpstream, }, cancellationToken); + /// + /// Validates. + /// public override IEnumerable Validate(ValidationContext validationContext) { foreach (var result in base.Validate(validationContext)) diff --git a/Proxytrace.Domain/CustomAnomaly/Internal/CustomAnomalyDetectorGenerator.cs b/Proxytrace.Domain/CustomAnomaly/Internal/CustomAnomalyDetectorGenerator.cs index 0ec8a4c55..651684431 100644 --- a/Proxytrace.Domain/CustomAnomaly/Internal/CustomAnomalyDetectorGenerator.cs +++ b/Proxytrace.Domain/CustomAnomaly/Internal/CustomAnomalyDetectorGenerator.cs @@ -9,6 +9,9 @@ internal class CustomAnomalyDetectorGenerator : DomainEntityGenerator + /// Initializes a new instance of the class. + /// public CustomAnomalyDetectorGenerator( ICustomAnomalyDetector.CreateNew factory, IRepository repository, @@ -19,6 +22,9 @@ public CustomAnomalyDetectorGenerator( this.agentGenerator = agentGenerator; } + /// + /// Generates asynchronously. + /// public override async Task GenerateAsync(CancellationToken cancellationToken = default) { // The review judge must be a system agent (mirrors the agentic evaluator generator). diff --git a/Proxytrace.Domain/CustomAnomaly/Internal/CustomAnomalyResult.cs b/Proxytrace.Domain/CustomAnomaly/Internal/CustomAnomalyResult.cs index fdf45adcb..d48060dcc 100644 --- a/Proxytrace.Domain/CustomAnomaly/Internal/CustomAnomalyResult.cs +++ b/Proxytrace.Domain/CustomAnomaly/Internal/CustomAnomalyResult.cs @@ -6,12 +6,30 @@ namespace Proxytrace.Domain.CustomAnomaly.Internal; internal record CustomAnomalyResult : DomainEntity, ICustomAnomalyResult { + /// + /// Gets the detector id. + /// public Guid DetectorId { get; } + /// + /// Gets the agent call id. + /// public Guid AgentCallId { get; } + /// + /// Gets the project id. + /// public Guid ProjectId { get; } + /// + /// Gets the matched trigger. + /// public string MatchedTrigger { get; } + /// + /// Gets the reasoning. + /// public string? Reasoning { get; } + /// + /// Initializes a new instance of the class. + /// public CustomAnomalyResult( Guid detectorId, Guid agentCallId, @@ -27,6 +45,9 @@ public CustomAnomalyResult( Reasoning = reasoning; } + /// + /// Initializes a new instance of the class. + /// public CustomAnomalyResult( Guid detectorId, Guid agentCallId, @@ -43,6 +64,9 @@ public CustomAnomalyResult( Reasoning = reasoning; } + /// + /// Validates. + /// public override IEnumerable Validate(ValidationContext validationContext) { foreach (var result in base.Validate(validationContext)) diff --git a/Proxytrace.Domain/CustomAnomaly/Internal/CustomAnomalyResultGenerator.cs b/Proxytrace.Domain/CustomAnomaly/Internal/CustomAnomalyResultGenerator.cs index cc6e2b4c9..320b1aa4e 100644 --- a/Proxytrace.Domain/CustomAnomaly/Internal/CustomAnomalyResultGenerator.cs +++ b/Proxytrace.Domain/CustomAnomaly/Internal/CustomAnomalyResultGenerator.cs @@ -10,6 +10,9 @@ internal class CustomAnomalyResultGenerator : DomainEntityGenerator detectorGenerator; private readonly IDomainEntityGenerator callGenerator; + /// + /// Initializes a new instance of the class. + /// public CustomAnomalyResultGenerator( ICustomAnomalyResult.CreateNew factory, IRepository repository, @@ -22,6 +25,9 @@ public CustomAnomalyResultGenerator( this.callGenerator = callGenerator; } + /// + /// Generates asynchronously. + /// public override async Task GenerateAsync(CancellationToken cancellationToken = default) { var detector = await detectorGenerator.GetOrCreateAsync(cancellationToken); diff --git a/Proxytrace.Domain/CustomAnomaly/TriggerMatcher.cs b/Proxytrace.Domain/CustomAnomaly/TriggerMatcher.cs index 8fb3d997b..dd4fa088a 100644 --- a/Proxytrace.Domain/CustomAnomaly/TriggerMatcher.cs +++ b/Proxytrace.Domain/CustomAnomaly/TriggerMatcher.cs @@ -27,6 +27,9 @@ public static class TriggerMatcher private const int MaxCachedRegexes = 256; private static readonly ConcurrentDictionary RegexCache = new(); + /// + /// Finds the first match. + /// public static TriggerMatch? FindFirstMatch(string text, IReadOnlyList triggers) { if (string.IsNullOrEmpty(text)) diff --git a/Proxytrace.Domain/Evaluation/EvaluationExtensions.cs b/Proxytrace.Domain/Evaluation/EvaluationExtensions.cs index d10850ae1..0884bbed6 100644 --- a/Proxytrace.Domain/Evaluation/EvaluationExtensions.cs +++ b/Proxytrace.Domain/Evaluation/EvaluationExtensions.cs @@ -13,6 +13,9 @@ internal static class EvaluationExtensions public static bool IsErrored(this IEvaluation evaluation) => !string.IsNullOrWhiteSpace(evaluation.ErrorMessage); + /// + /// Combines the scores. + /// public static EvaluationScore? CombineScores(this IReadOnlyCollection evaluations) { var scored = evaluations diff --git a/Proxytrace.Domain/Evaluation/EvaluationScore.cs b/Proxytrace.Domain/Evaluation/EvaluationScore.cs index a459a52d4..2d9a0d55d 100644 --- a/Proxytrace.Domain/Evaluation/EvaluationScore.cs +++ b/Proxytrace.Domain/Evaluation/EvaluationScore.cs @@ -1,5 +1,8 @@ namespace Proxytrace.Domain.Evaluation; +/// +/// Specifies the evaluation score. +/// public enum EvaluationScore : byte { Terrible = 1, diff --git a/Proxytrace.Domain/Evaluation/IEvaluation.cs b/Proxytrace.Domain/Evaluation/IEvaluation.cs index 530340d92..acc5c4784 100644 --- a/Proxytrace.Domain/Evaluation/IEvaluation.cs +++ b/Proxytrace.Domain/Evaluation/IEvaluation.cs @@ -3,8 +3,14 @@ namespace Proxytrace.Domain.Evaluation; +/// +/// Represents a evaluation. +/// public interface IEvaluation : IDomainObject { + /// + /// Factory delegate for creating a new instance. + /// public delegate IEvaluation Create( IEvaluator evaluator, EvaluationScore score, @@ -13,6 +19,9 @@ public delegate IEvaluation Create( decimal? cost = null, string? reasoning = null); + /// + /// Factory delegate for creating a new errored instance. + /// public delegate IEvaluation CreateErrored( IEvaluator evaluator, TimeSpan latency, diff --git a/Proxytrace.Domain/Evaluation/Internal/Evaluation.cs b/Proxytrace.Domain/Evaluation/Internal/Evaluation.cs index 035c14122..9b73eff9e 100644 --- a/Proxytrace.Domain/Evaluation/Internal/Evaluation.cs +++ b/Proxytrace.Domain/Evaluation/Internal/Evaluation.cs @@ -7,19 +7,46 @@ namespace Proxytrace.Domain.Evaluation.Internal; internal sealed record Evaluation : IEvaluation { + /// + /// Gets the evaluator. + /// public IEvaluator Evaluator { get; } + /// + /// Gets the score. + /// public EvaluationScore? Score { get; } + /// + /// The passed. + /// public bool Passed => string.IsNullOrWhiteSpace(ErrorMessage) && Score is >= EvaluationScore.Acceptable; + /// + /// Gets the reasoning. + /// public string? Reasoning { get; } + /// + /// Gets the error message. + /// public string? ErrorMessage { get; } + /// + /// Gets the latency. + /// public TimeSpan Latency { get; } + /// + /// Gets the token usage. + /// public TokenUsage? TokenUsage { get; } + /// + /// Gets the cost. + /// public decimal? Cost { get; } + /// + /// Initializes a new instance of the class. + /// public Evaluation( IEvaluator evaluator, EvaluationScore score, @@ -37,6 +64,9 @@ public Evaluation( ErrorMessage = null; } + /// + /// Initializes a new instance of the class. + /// public Evaluation( IEvaluator evaluator, TimeSpan latency, @@ -53,6 +83,9 @@ public Evaluation( : $"{exception.GetType().Name}: {exception.Message}"; } + /// + /// Validates. + /// public IEnumerable Validate(ValidationContext validationContext) { foreach (var validationResult in Evaluator.Validate(validationContext)) diff --git a/Proxytrace.Domain/Evaluation/Internal/EvaluationGenerator.cs b/Proxytrace.Domain/Evaluation/Internal/EvaluationGenerator.cs index 77935b703..ee1458cca 100644 --- a/Proxytrace.Domain/Evaluation/Internal/EvaluationGenerator.cs +++ b/Proxytrace.Domain/Evaluation/Internal/EvaluationGenerator.cs @@ -10,6 +10,9 @@ internal class EvaluationGenerator : DomainObjectGenerator private readonly IEvaluation.Create factory; private readonly IEvaluation.CreateErrored erroredFactory; + /// + /// Initializes a new instance of the class. + /// public EvaluationGenerator( IRandom random, IDomainEntityGenerator evaluatorGenerator, @@ -21,6 +24,9 @@ public EvaluationGenerator( this.erroredFactory = erroredFactory; } + /// + /// Creates asynchronously. + /// public override async Task CreateAsync(CancellationToken cancellationToken = default) { IEvaluator evaluator = await evaluatorGenerator.GetOrCreateAsync(cancellationToken); @@ -31,6 +37,9 @@ public override async Task CreateAsync(CancellationToken cancellati reasoning: random.String()); } + /// + /// Creates the errored asynchronously. + /// public async Task CreateErroredAsync(CancellationToken cancellationToken = default) { IEvaluator evaluator = await evaluatorGenerator.GetOrCreateAsync(cancellationToken); diff --git a/Proxytrace.Domain/Evaluation/Internal/StoredEvaluationException.cs b/Proxytrace.Domain/Evaluation/Internal/StoredEvaluationException.cs index 08236b2ac..39ed7c622 100644 --- a/Proxytrace.Domain/Evaluation/Internal/StoredEvaluationException.cs +++ b/Proxytrace.Domain/Evaluation/Internal/StoredEvaluationException.cs @@ -7,6 +7,9 @@ namespace Proxytrace.Domain.Evaluation.Internal; /// internal sealed class StoredEvaluationException : Exception { + /// + /// Initializes a new instance of the class. + /// public StoredEvaluationException(string message) : base(message) { } diff --git a/Proxytrace.Domain/Evaluator/EvaluatorKind.cs b/Proxytrace.Domain/Evaluator/EvaluatorKind.cs index 98c920cb5..d1d70fd71 100644 --- a/Proxytrace.Domain/Evaluator/EvaluatorKind.cs +++ b/Proxytrace.Domain/Evaluator/EvaluatorKind.cs @@ -1,5 +1,8 @@ namespace Proxytrace.Domain.Evaluator; +/// +/// Specifies the evaluator kind. +/// public enum EvaluatorKind { Agentic = 0, diff --git a/Proxytrace.Domain/Evaluator/IAgenticEvaluator.cs b/Proxytrace.Domain/Evaluator/IAgenticEvaluator.cs index 08204ba00..d5fb17aba 100644 --- a/Proxytrace.Domain/Evaluator/IAgenticEvaluator.cs +++ b/Proxytrace.Domain/Evaluator/IAgenticEvaluator.cs @@ -2,12 +2,24 @@ namespace Proxytrace.Domain.Evaluator; +/// +/// Represents a agentic evaluator. +/// public interface IAgenticEvaluator : IEvaluator { + /// + /// Gets the agent. + /// public IAgent Agent { get; } + /// + /// Factory delegate for creating a new new instance. + /// public delegate IAgenticEvaluator CreateNew(IAgent agent); + /// + /// Factory delegate for creating a new existing instance. + /// public delegate IAgenticEvaluator CreateExisting( IAgent agent, IDomainEntityData existing); diff --git a/Proxytrace.Domain/Evaluator/IEvaluatorGenerator.cs b/Proxytrace.Domain/Evaluator/IEvaluatorGenerator.cs index 6c3c361cb..339f2ddc7 100644 --- a/Proxytrace.Domain/Evaluator/IEvaluatorGenerator.cs +++ b/Proxytrace.Domain/Evaluator/IEvaluatorGenerator.cs @@ -1,5 +1,8 @@ namespace Proxytrace.Domain.Evaluator; +/// +/// Generates evaluator instances. +/// public interface IEvaluatorGenerator : IDomainEntityGenerator { Task CreateAsync(EvaluatorKind kind, CancellationToken cancellationToken = default); diff --git a/Proxytrace.Domain/Evaluator/IExactMatchEvaluator.cs b/Proxytrace.Domain/Evaluator/IExactMatchEvaluator.cs index 83a74e436..c25d78e4d 100644 --- a/Proxytrace.Domain/Evaluator/IExactMatchEvaluator.cs +++ b/Proxytrace.Domain/Evaluator/IExactMatchEvaluator.cs @@ -2,9 +2,18 @@ namespace Proxytrace.Domain.Evaluator; +/// +/// Represents a exact match evaluator. +/// public interface IExactMatchEvaluator : IEvaluator { + /// + /// Factory delegate for creating a new new instance. + /// public delegate IExactMatchEvaluator CreateNew(IProject project); + /// + /// Factory delegate for creating a new existing instance. + /// public delegate IExactMatchEvaluator CreateExisting( IProject project, IDomainEntityData existing); diff --git a/Proxytrace.Domain/Evaluator/IJsonSchemaMatchEvaluator.cs b/Proxytrace.Domain/Evaluator/IJsonSchemaMatchEvaluator.cs index dfa9b283c..2b2096aaa 100644 --- a/Proxytrace.Domain/Evaluator/IJsonSchemaMatchEvaluator.cs +++ b/Proxytrace.Domain/Evaluator/IJsonSchemaMatchEvaluator.cs @@ -9,10 +9,16 @@ public interface IJsonSchemaMatchEvaluator : IEvaluator { string JsonSchema { get; } + /// + /// Factory delegate for creating a new new instance. + /// public delegate IJsonSchemaMatchEvaluator CreateNew( string jsonSchema, IProject project); + /// + /// Factory delegate for creating a new existing instance. + /// public delegate IJsonSchemaMatchEvaluator CreateExisting( string jsonSchema, IProject project, diff --git a/Proxytrace.Domain/Evaluator/INumericMatchEvaluator.cs b/Proxytrace.Domain/Evaluator/INumericMatchEvaluator.cs index e4bf56662..4117b54c7 100644 --- a/Proxytrace.Domain/Evaluator/INumericMatchEvaluator.cs +++ b/Proxytrace.Domain/Evaluator/INumericMatchEvaluator.cs @@ -8,11 +8,17 @@ namespace Proxytrace.Domain.Evaluator; /// public interface INumericMatchEvaluator : IEvaluator { + /// + /// Factory delegate for creating a new new instance. + /// public delegate INumericMatchEvaluator CreateNew( Regex extractionPattern, decimal tolerance, IProject project); + /// + /// Factory delegate for creating a new existing instance. + /// public delegate INumericMatchEvaluator CreateExisting( Regex extractionPattern, decimal tolerance, diff --git a/Proxytrace.Domain/Evaluator/Internal/AgenticEvaluator.cs b/Proxytrace.Domain/Evaluator/Internal/AgenticEvaluator.cs index a1b87a990..abe7bba9c 100644 --- a/Proxytrace.Domain/Evaluator/Internal/AgenticEvaluator.cs +++ b/Proxytrace.Domain/Evaluator/Internal/AgenticEvaluator.cs @@ -21,16 +21,31 @@ internal sealed record AgenticEvaluator : DomainEntity, IAgenticEval private readonly IEvaluation.Create evaluationFactory; private readonly IEvaluation.CreateErrored erroredFactory; + /// + /// Gets the agent. + /// public IAgent Agent { get; } + /// + /// Gets the name. + /// public string Name => Agent.Name; + /// + /// Provides additional functionality. + /// public EvaluatorKind Kind => EvaluatorKind.Agentic; + /// + /// Provides additional functionality. + /// public IProject Project => Agent.Project; + /// + /// Initializes a new instance of the class. + /// public AgenticEvaluator( IAgent agent, IEvaluation.Create evaluationFactory, @@ -42,6 +57,9 @@ public AgenticEvaluator( this.erroredFactory = erroredFactory; } + /// + /// Initializes a new instance of the class. + /// public AgenticEvaluator( IAgent agent, IDomainEntityData existing, @@ -54,6 +72,9 @@ public AgenticEvaluator( this.erroredFactory = erroredFactory; } + /// + /// Evaluates asynchronously. + /// public async Task EvaluateAsync(ITestResult testResult, CancellationToken cancellationToken = default) { Stopwatch sw = Stopwatch.StartNew(); @@ -124,6 +145,9 @@ private UserMessage BuildEvaluationMessage(ITestResult testResult) return Message.CreateUserMessage(content); } + /// + /// Validates. + /// public override IEnumerable Validate(ValidationContext validationContext) { foreach (var validationResult in base.Validate(validationContext)) diff --git a/Proxytrace.Domain/Evaluator/Internal/AgenticEvaluatorGenerator.cs b/Proxytrace.Domain/Evaluator/Internal/AgenticEvaluatorGenerator.cs index de0b5ada3..d824b2b2d 100644 --- a/Proxytrace.Domain/Evaluator/Internal/AgenticEvaluatorGenerator.cs +++ b/Proxytrace.Domain/Evaluator/Internal/AgenticEvaluatorGenerator.cs @@ -9,6 +9,9 @@ internal class AgenticEvaluatorGenerator : EvaluatorGeneratorBase + /// Initializes a new instance of the class. + /// public AgenticEvaluatorGenerator( IAgentGenerator agentGenerator, IAgenticEvaluator.CreateNew factory, @@ -20,6 +23,9 @@ public AgenticEvaluatorGenerator( this.random = random; } + /// + /// Generates asynchronously. + /// public override async Task GenerateAsync(CancellationToken cancellationToken = default) { IAgent agent = await agentGenerator.CreateAsync(random.String(), isSystemAgent: true, cancellationToken: cancellationToken); diff --git a/Proxytrace.Domain/Evaluator/Internal/EvaluatorGenerator.cs b/Proxytrace.Domain/Evaluator/Internal/EvaluatorGenerator.cs index 9bf60377b..8e7789014 100644 --- a/Proxytrace.Domain/Evaluator/Internal/EvaluatorGenerator.cs +++ b/Proxytrace.Domain/Evaluator/Internal/EvaluatorGenerator.cs @@ -13,6 +13,9 @@ internal class EvaluatorGenerator : DomainEntityGenerator, IEvaluato private readonly IDomainEntityGenerator numericMatchGenerator; private readonly IDomainEntityGenerator jsonSchemaMatchGenerator; + /// + /// Initializes a new instance of the class. + /// public EvaluatorGenerator( IExactMatchEvaluator.CreateNew factory, IRepository repository, @@ -31,12 +34,18 @@ public EvaluatorGenerator( this.jsonSchemaMatchGenerator = jsonSchemaMatchGenerator; } + /// + /// Generates asynchronously. + /// public override async Task GenerateAsync(CancellationToken cancellationToken = default) { var project = await projectGenerator.GetOrCreateAsync(cancellationToken); return factory(project); } + /// + /// Creates asynchronously. + /// public async Task CreateAsync(EvaluatorKind kind, CancellationToken cancellationToken = default) => kind switch { diff --git a/Proxytrace.Domain/Evaluator/Internal/EvaluatorGeneratorBase.cs b/Proxytrace.Domain/Evaluator/Internal/EvaluatorGeneratorBase.cs index bc3962047..0e6c91397 100644 --- a/Proxytrace.Domain/Evaluator/Internal/EvaluatorGeneratorBase.cs +++ b/Proxytrace.Domain/Evaluator/Internal/EvaluatorGeneratorBase.cs @@ -10,14 +10,23 @@ protected EvaluatorGeneratorBase(IRepository repository) this.repository = repository; } + /// + /// Generates asynchronously. + /// public abstract Task GenerateAsync(CancellationToken cancellationToken = default); + /// + /// Creates asynchronously. + /// public async Task CreateAsync(CancellationToken cancellationToken = default) { var instance = await GenerateAsync(cancellationToken); return (T)await repository.AddAsync(instance, cancellationToken); } + /// + /// Gets the or create asynchronously. + /// public async Task GetOrCreateAsync(CancellationToken cancellationToken = default) { var existing = await repository.FindFirstAsync(cancellationToken); diff --git a/Proxytrace.Domain/Evaluator/Internal/ExactMatchEvaluator.cs b/Proxytrace.Domain/Evaluator/Internal/ExactMatchEvaluator.cs index 15ab72507..dd13685a9 100644 --- a/Proxytrace.Domain/Evaluator/Internal/ExactMatchEvaluator.cs +++ b/Proxytrace.Domain/Evaluator/Internal/ExactMatchEvaluator.cs @@ -14,14 +14,26 @@ internal record ExactMatchEvaluator : DomainEntity, IExactMatchEvalu { private readonly IEvaluation.Create evaluationFactory; + /// + /// Provides additional functionality. + /// public string Name => "Exact Match"; + /// + /// Provides additional functionality. + /// public EvaluatorKind Kind => EvaluatorKind.ExactMatch; + /// + /// Gets the project. + /// public IProject Project { get; } + /// + /// Initializes a new instance of the class. + /// public ExactMatchEvaluator( IProject project, IEvaluation.Create evaluationFactory, @@ -31,6 +43,9 @@ public ExactMatchEvaluator( this.evaluationFactory = evaluationFactory; } + /// + /// Initializes a new instance of the class. + /// public ExactMatchEvaluator( IProject project, IDomainEntityData existing, @@ -88,6 +103,9 @@ public ExactMatchEvaluator( return Task.FromResult(evaluation); } + /// + /// Validates. + /// public override IEnumerable Validate(ValidationContext validationContext) { foreach (var result in base.Validate(validationContext)) diff --git a/Proxytrace.Domain/Evaluator/Internal/ExactMatchEvaluatorGenerator.cs b/Proxytrace.Domain/Evaluator/Internal/ExactMatchEvaluatorGenerator.cs index bdb965324..ce0b3f25a 100644 --- a/Proxytrace.Domain/Evaluator/Internal/ExactMatchEvaluatorGenerator.cs +++ b/Proxytrace.Domain/Evaluator/Internal/ExactMatchEvaluatorGenerator.cs @@ -7,6 +7,9 @@ internal class ExactMatchEvaluatorGenerator : EvaluatorGeneratorBase projectGenerator; + /// + /// Initializes a new instance of the class. + /// public ExactMatchEvaluatorGenerator( IExactMatchEvaluator.CreateNew factory, IDomainEntityGenerator projectGenerator, @@ -16,6 +19,9 @@ public ExactMatchEvaluatorGenerator( this.projectGenerator = projectGenerator; } + /// + /// Generates asynchronously. + /// public override async Task GenerateAsync(CancellationToken cancellationToken = default) { IProject project = await projectGenerator.GetOrCreateAsync(cancellationToken); diff --git a/Proxytrace.Domain/Evaluator/Internal/JsonSchemaMatchEvaluator.cs b/Proxytrace.Domain/Evaluator/Internal/JsonSchemaMatchEvaluator.cs index 90e46d0da..1f147f218 100644 --- a/Proxytrace.Domain/Evaluator/Internal/JsonSchemaMatchEvaluator.cs +++ b/Proxytrace.Domain/Evaluator/Internal/JsonSchemaMatchEvaluator.cs @@ -16,16 +16,31 @@ internal record JsonSchemaMatchEvaluator : DomainEntity, IJsonSchema { private readonly IEvaluation.Create evaluationFactory; + /// + /// Provides additional functionality. + /// public string Name => "Json Schema Match"; + /// + /// Provides additional functionality. + /// public EvaluatorKind Kind => EvaluatorKind.JsonSchemaMatch; + /// + /// Gets the project. + /// public IProject Project { get; } + /// + /// Gets the json schema. + /// public string JsonSchema { get; } + /// + /// Initializes a new instance of the class. + /// public JsonSchemaMatchEvaluator( string jsonSchema, IProject project, @@ -37,6 +52,9 @@ public JsonSchemaMatchEvaluator( this.evaluationFactory = evaluationFactory; } + /// + /// Initializes a new instance of the class. + /// public JsonSchemaMatchEvaluator( string jsonSchema, IProject project, @@ -49,6 +67,9 @@ public JsonSchemaMatchEvaluator( this.evaluationFactory = evaluationFactory; } + /// + /// Evaluates asynchronously. + /// public Task EvaluateAsync( ITestResult testResult, CancellationToken cancellationToken = default) @@ -88,6 +109,9 @@ public JsonSchemaMatchEvaluator( return Task.FromResult(evaluationFactory(this, score, sw.Elapsed, reasoning: reasoning)); } + /// + /// Validates. + /// public override IEnumerable Validate(ValidationContext validationContext) { foreach (var result in base.Validate(validationContext)) diff --git a/Proxytrace.Domain/Evaluator/Internal/JsonSchemaMatchEvaluatorGenerator.cs b/Proxytrace.Domain/Evaluator/Internal/JsonSchemaMatchEvaluatorGenerator.cs index f5645aec2..632d9a4e8 100644 --- a/Proxytrace.Domain/Evaluator/Internal/JsonSchemaMatchEvaluatorGenerator.cs +++ b/Proxytrace.Domain/Evaluator/Internal/JsonSchemaMatchEvaluatorGenerator.cs @@ -7,6 +7,9 @@ internal class JsonSchemaMatchEvaluatorGenerator : EvaluatorGeneratorBase projectGenerator; + /// + /// Initializes a new instance of the class. + /// public JsonSchemaMatchEvaluatorGenerator( IJsonSchemaMatchEvaluator.CreateNew factory, IDomainEntityGenerator projectGenerator, @@ -16,6 +19,9 @@ public JsonSchemaMatchEvaluatorGenerator( this.projectGenerator = projectGenerator; } + /// + /// Generates asynchronously. + /// public override async Task GenerateAsync(CancellationToken cancellationToken = default) { IProject project = await projectGenerator.GetOrCreateAsync(cancellationToken); diff --git a/Proxytrace.Domain/Evaluator/Internal/NumericMatchEvaluator.cs b/Proxytrace.Domain/Evaluator/Internal/NumericMatchEvaluator.cs index ecf1f1ce7..4f55d886a 100644 --- a/Proxytrace.Domain/Evaluator/Internal/NumericMatchEvaluator.cs +++ b/Proxytrace.Domain/Evaluator/Internal/NumericMatchEvaluator.cs @@ -16,17 +16,35 @@ internal record NumericMatchEvaluator : DomainEntity, INumericMatchE { private readonly IEvaluation.Create evaluationFactory; + /// + /// Provides additional functionality. + /// public string Name => "Numeric Match"; + /// + /// Provides additional functionality. + /// public EvaluatorKind Kind => EvaluatorKind.NumericMatch; + /// + /// Gets the project. + /// public IProject Project { get; } + /// + /// Gets the extraction pattern. + /// public Regex ExtractionPattern { get; } + /// + /// Gets the tolerance. + /// public decimal Tolerance { get; } + /// + /// Initializes a new instance of the class. + /// public NumericMatchEvaluator( Regex extractionPattern, decimal tolerance, @@ -40,6 +58,9 @@ public NumericMatchEvaluator( this.evaluationFactory = evaluationFactory; } + /// + /// Initializes a new instance of the class. + /// public NumericMatchEvaluator( Regex extractionPattern, decimal tolerance, @@ -54,6 +75,9 @@ public NumericMatchEvaluator( this.evaluationFactory = evaluationFactory; } + /// + /// Evaluates asynchronously. + /// public Task EvaluateAsync( ITestResult testResult, CancellationToken cancellationToken = default) @@ -108,6 +132,9 @@ private static bool TryParseInvariant(string value, out decimal result) CultureInfo.InvariantCulture, out result); + /// + /// Validates. + /// public override IEnumerable Validate(ValidationContext validationContext) { foreach (var result in base.Validate(validationContext)) diff --git a/Proxytrace.Domain/Evaluator/Internal/NumericMatchEvaluatorGenerator.cs b/Proxytrace.Domain/Evaluator/Internal/NumericMatchEvaluatorGenerator.cs index c829113f8..6a4d7d1ae 100644 --- a/Proxytrace.Domain/Evaluator/Internal/NumericMatchEvaluatorGenerator.cs +++ b/Proxytrace.Domain/Evaluator/Internal/NumericMatchEvaluatorGenerator.cs @@ -8,6 +8,9 @@ internal class NumericMatchEvaluatorGenerator : EvaluatorGeneratorBase projectGenerator; + /// + /// Initializes a new instance of the class. + /// public NumericMatchEvaluatorGenerator( INumericMatchEvaluator.CreateNew factory, IDomainEntityGenerator projectGenerator, @@ -17,6 +20,9 @@ public NumericMatchEvaluatorGenerator( this.projectGenerator = projectGenerator; } + /// + /// Generates asynchronously. + /// public override async Task GenerateAsync(CancellationToken cancellationToken = default) { IProject project = await projectGenerator.GetOrCreateAsync(cancellationToken); diff --git a/Proxytrace.Domain/Invite/IInvite.cs b/Proxytrace.Domain/Invite/IInvite.cs index 89cdc0951..4e95ea51c 100644 --- a/Proxytrace.Domain/Invite/IInvite.cs +++ b/Proxytrace.Domain/Invite/IInvite.cs @@ -35,6 +35,12 @@ public interface IInvite : IDomainEntity /// Marks the invite as redeemed and persists. Task MarkConsumedAsync(CancellationToken cancellationToken = default); + /// + /// Factory delegate for creating a new new instance. + /// public delegate IInvite CreateNew(string email, UserRole role, string tokenHash, DateTimeOffset expiresAt, IUser invitedBy); + /// + /// Factory delegate for creating a new existing instance. + /// public delegate IInvite CreateExisting(string email, UserRole role, string tokenHash, DateTimeOffset expiresAt, DateTimeOffset? consumedAt, IUser invitedBy, IDomainEntityData existing); } diff --git a/Proxytrace.Domain/Invite/IInviteRepository.cs b/Proxytrace.Domain/Invite/IInviteRepository.cs index 31a1bcb27..19e51c629 100644 --- a/Proxytrace.Domain/Invite/IInviteRepository.cs +++ b/Proxytrace.Domain/Invite/IInviteRepository.cs @@ -1,5 +1,8 @@ namespace Proxytrace.Domain.Invite; +/// +/// Repository for persisting and querying invite entities. +/// public interface IInviteRepository : IRepository { Task FindByTokenAsync(string token, CancellationToken cancellationToken = default); diff --git a/Proxytrace.Domain/Invite/Internal/Invite.cs b/Proxytrace.Domain/Invite/Internal/Invite.cs index 55433db06..9bc0cf69f 100644 --- a/Proxytrace.Domain/Invite/Internal/Invite.cs +++ b/Proxytrace.Domain/Invite/Internal/Invite.cs @@ -7,13 +7,34 @@ namespace Proxytrace.Domain.Invite.Internal; internal record Invite : DomainEntity, IInvite { + /// + /// Gets the email. + /// public string Email { get; } + /// + /// Gets the role. + /// public UserRole Role { get; } + /// + /// Gets the token hash. + /// public string TokenHash { get; } + /// + /// Gets the expires at. + /// public DateTimeOffset ExpiresAt { get; } + /// + /// Gets or sets the consumed at. + /// public DateTimeOffset? ConsumedAt { get; private init; } + /// + /// Gets the invited by. + /// public IUser InvitedBy { get; } + /// + /// Initializes a new instance of the class. + /// public Invite( string email, UserRole role, @@ -29,6 +50,9 @@ public Invite( InvitedBy = invitedBy; } + /// + /// Initializes a new instance of the class. + /// public Invite( string email, UserRole role, @@ -47,6 +71,9 @@ public Invite( InvitedBy = invitedBy; } + /// + /// Mark consumed asynchronously. + /// public Task MarkConsumedAsync(CancellationToken cancellationToken = default) { if (ConsumedAt is not null) @@ -56,6 +83,9 @@ public Task MarkConsumedAsync(CancellationToken cancellationToken = def return ApplyAsync(this with { ConsumedAt = DateTimeOffset.UtcNow }, cancellationToken); } + /// + /// Validates. + /// public override IEnumerable Validate(ValidationContext validationContext) { foreach (var result in base.Validate(validationContext)) diff --git a/Proxytrace.Domain/Invite/Internal/InviteGenerator.cs b/Proxytrace.Domain/Invite/Internal/InviteGenerator.cs index c62733c48..a6e5a077b 100644 --- a/Proxytrace.Domain/Invite/Internal/InviteGenerator.cs +++ b/Proxytrace.Domain/Invite/Internal/InviteGenerator.cs @@ -10,6 +10,9 @@ internal class InviteGenerator : DomainEntityGenerator private readonly IInvite.CreateNew factory; private readonly IDomainEntityGenerator users; + /// + /// Initializes a new instance of the class. + /// public InviteGenerator( IInvite.CreateNew factory, IDomainEntityGenerator users, @@ -20,6 +23,9 @@ public InviteGenerator( this.users = users; } + /// + /// Generates asynchronously. + /// public override async Task GenerateAsync(CancellationToken cancellationToken = default) { var user = await users.CreateAsync(cancellationToken); diff --git a/Proxytrace.Domain/Kiosk/KioskOptions.cs b/Proxytrace.Domain/Kiosk/KioskOptions.cs index 7e4156cc9..6e68587ae 100644 --- a/Proxytrace.Domain/Kiosk/KioskOptions.cs +++ b/Proxytrace.Domain/Kiosk/KioskOptions.cs @@ -1,9 +1,21 @@ namespace Proxytrace.Domain.Kiosk; +/// +/// Configuration options for kiosk. +/// public sealed record KioskOptions { + /// + /// Gets or sets the enabled. + /// public bool Enabled { get; init; } + /// + /// Gets or sets the demo user email. + /// public string DemoUserEmail { get; init; } = "demo@proxytrace.dev"; + /// + /// Gets or sets the demo user name. + /// public string DemoUserName { get; init; } = "Demo Visitor"; /// diff --git a/Proxytrace.Domain/MfaBackupCode/IMfaBackupCode.cs b/Proxytrace.Domain/MfaBackupCode/IMfaBackupCode.cs index e271b62c4..3c604dbf9 100644 --- a/Proxytrace.Domain/MfaBackupCode/IMfaBackupCode.cs +++ b/Proxytrace.Domain/MfaBackupCode/IMfaBackupCode.cs @@ -34,6 +34,12 @@ public interface IMfaBackupCode : IDomainEntity /// Marks the code as redeemed and persists. Task MarkConsumedAsync(CancellationToken cancellationToken = default); + /// + /// Factory delegate for creating a new new instance. + /// public delegate IMfaBackupCode CreateNew(IUser user, string codeHash); + /// + /// Factory delegate for creating a new existing instance. + /// public delegate IMfaBackupCode CreateExisting(IUser user, string codeHash, DateTimeOffset? consumedAt, IDomainEntityData existing); } diff --git a/Proxytrace.Domain/MfaBackupCode/IMfaBackupCodeRepository.cs b/Proxytrace.Domain/MfaBackupCode/IMfaBackupCodeRepository.cs index 140d94b54..59a2ca99a 100644 --- a/Proxytrace.Domain/MfaBackupCode/IMfaBackupCodeRepository.cs +++ b/Proxytrace.Domain/MfaBackupCode/IMfaBackupCodeRepository.cs @@ -1,5 +1,8 @@ namespace Proxytrace.Domain.MfaBackupCode; +/// +/// Repository for persisting and querying mfa backup code entities. +/// public interface IMfaBackupCodeRepository : IRepository { /// All backup codes (used and unused) belonging to the user. diff --git a/Proxytrace.Domain/MfaBackupCode/Internal/MfaBackupCode.cs b/Proxytrace.Domain/MfaBackupCode/Internal/MfaBackupCode.cs index d9074e215..6585d0d2f 100644 --- a/Proxytrace.Domain/MfaBackupCode/Internal/MfaBackupCode.cs +++ b/Proxytrace.Domain/MfaBackupCode/Internal/MfaBackupCode.cs @@ -7,10 +7,22 @@ namespace Proxytrace.Domain.MfaBackupCode.Internal; internal record MfaBackupCode : DomainEntity, IMfaBackupCode { + /// + /// Gets the user. + /// public IUser User { get; } + /// + /// Gets the code hash. + /// public string CodeHash { get; } + /// + /// Gets or sets the consumed at. + /// public DateTimeOffset? ConsumedAt { get; private init; } + /// + /// Initializes a new instance of the class. + /// public MfaBackupCode( IUser user, string codeHash, @@ -20,6 +32,9 @@ public MfaBackupCode( CodeHash = codeHash; } + /// + /// Initializes a new instance of the class. + /// public MfaBackupCode( IUser user, string codeHash, @@ -32,6 +47,9 @@ public MfaBackupCode( ConsumedAt = consumedAt; } + /// + /// Mark consumed asynchronously. + /// public Task MarkConsumedAsync(CancellationToken cancellationToken = default) { if (ConsumedAt is not null) @@ -41,6 +59,9 @@ public Task MarkConsumedAsync(CancellationToken cancellationToke return ApplyAsync(this with { ConsumedAt = DateTimeOffset.UtcNow }, cancellationToken); } + /// + /// Validates. + /// public override IEnumerable Validate(ValidationContext validationContext) { foreach (var result in base.Validate(validationContext)) diff --git a/Proxytrace.Domain/MfaBackupCode/Internal/MfaBackupCodeGenerator.cs b/Proxytrace.Domain/MfaBackupCode/Internal/MfaBackupCodeGenerator.cs index 9e77e2b85..d29e70e71 100644 --- a/Proxytrace.Domain/MfaBackupCode/Internal/MfaBackupCodeGenerator.cs +++ b/Proxytrace.Domain/MfaBackupCode/Internal/MfaBackupCodeGenerator.cs @@ -10,6 +10,9 @@ internal class MfaBackupCodeGenerator : DomainEntityGenerator private readonly IMfaBackupCode.CreateNew factory; private readonly IDomainEntityGenerator users; + /// + /// Initializes a new instance of the class. + /// public MfaBackupCodeGenerator( IMfaBackupCode.CreateNew factory, IDomainEntityGenerator users, @@ -20,6 +23,9 @@ public MfaBackupCodeGenerator( this.users = users; } + /// + /// Generates asynchronously. + /// public override async Task GenerateAsync(CancellationToken cancellationToken = default) { var user = await users.GetOrCreateAsync(cancellationToken); diff --git a/Proxytrace.Domain/Model/IModelRepository.cs b/Proxytrace.Domain/Model/IModelRepository.cs index 6802941e2..2295faf53 100644 --- a/Proxytrace.Domain/Model/IModelRepository.cs +++ b/Proxytrace.Domain/Model/IModelRepository.cs @@ -1,5 +1,8 @@ namespace Proxytrace.Domain.Model; +/// +/// Repository for persisting and querying model entities. +/// public interface IModelRepository : IRepository { Task GetOrCreateAsync(string name, CancellationToken cancellationToken = default); diff --git a/Proxytrace.Domain/Model/Internal/Model.cs b/Proxytrace.Domain/Model/Internal/Model.cs index cdbb257d9..02e84ae28 100644 --- a/Proxytrace.Domain/Model/Internal/Model.cs +++ b/Proxytrace.Domain/Model/Internal/Model.cs @@ -6,18 +6,30 @@ namespace Proxytrace.Domain.Model.Internal; internal record Model : DomainEntity, IModel { + /// + /// Gets the name. + /// public string Name { get; } + /// + /// Initializes a new instance of the class. + /// public Model(string name, IRepository repository) : base(repository) { Name = name; } + /// + /// Initializes a new instance of the class. + /// public Model(string name, IDomainEntityData existing, IRepository repository) : base(existing, repository) { Name = name; } + /// + /// Validates. + /// public override IEnumerable Validate(ValidationContext validationContext) { foreach (var result in base.Validate(validationContext)) diff --git a/Proxytrace.Domain/Model/Internal/ModelGenerator.cs b/Proxytrace.Domain/Model/Internal/ModelGenerator.cs index 85d876c02..c61b2ec2b 100644 --- a/Proxytrace.Domain/Model/Internal/ModelGenerator.cs +++ b/Proxytrace.Domain/Model/Internal/ModelGenerator.cs @@ -18,6 +18,9 @@ internal class ModelGenerator : DomainEntityGenerator private readonly IModel.CreateNew factory; + /// + /// Initializes a new instance of the class. + /// public ModelGenerator( IModel.CreateNew factory, IRepository repository, @@ -26,6 +29,9 @@ public ModelGenerator( this.factory = factory; } + /// + /// Generates asynchronously. + /// public override Task GenerateAsync(CancellationToken cancellationToken = default) => factory(name: $"{random.Any(ModelNames)}-{random.UniqueString()}").ToTaskResult(); } diff --git a/Proxytrace.Domain/ModelEndpoint/AgentExtensions.cs b/Proxytrace.Domain/ModelEndpoint/AgentExtensions.cs index 6f634c5e4..1d789c3dc 100644 --- a/Proxytrace.Domain/ModelEndpoint/AgentExtensions.cs +++ b/Proxytrace.Domain/ModelEndpoint/AgentExtensions.cs @@ -9,6 +9,9 @@ namespace Proxytrace.Domain.ModelEndpoint; /// public static class ModelClientExtensions { + /// + /// Completes asynchronously. + /// public static Task CompleteAsync( this IModelClient client, UserMessage userMessage, @@ -24,6 +27,9 @@ public static Task CompleteAsync( cancellationToken); } + /// + /// Completes asynchronously. + /// public static async Task CompleteAsync( this IModelClient client, UserMessage userMessage, diff --git a/Proxytrace.Domain/ModelEndpoint/Internal/ModelEndpoint.cs b/Proxytrace.Domain/ModelEndpoint/Internal/ModelEndpoint.cs index 77f579a31..fd7b228a7 100644 --- a/Proxytrace.Domain/ModelEndpoint/Internal/ModelEndpoint.cs +++ b/Proxytrace.Domain/ModelEndpoint/Internal/ModelEndpoint.cs @@ -9,12 +9,30 @@ namespace Proxytrace.Domain.ModelEndpoint.Internal; internal record ModelEndpoint : DomainEntity, IModelEndpoint { + /// + /// Gets the model. + /// public IModel Model { get; } + /// + /// Gets the provider. + /// public IModelProvider Provider { get; } + /// + /// Gets the input token cost. + /// public decimal? InputTokenCost { get; } + /// + /// Gets the output token cost. + /// public decimal? OutputTokenCost { get; } + /// + /// Gets the cached input token cost. + /// public decimal? CachedInputTokenCost { get; } + /// + /// Initializes a new instance of the class. + /// public ModelEndpoint( IModel model, IModelProvider provider, @@ -30,6 +48,9 @@ public ModelEndpoint( CachedInputTokenCost = cachedInputTokenCost; } + /// + /// Initializes a new instance of the class. + /// public ModelEndpoint( IModel model, IModelProvider provider, @@ -66,6 +87,9 @@ public ModelEndpoint( 1_000_000m; } + /// + /// Validates. + /// public override IEnumerable Validate(ValidationContext validationContext) { foreach (var result in base.Validate(validationContext)) diff --git a/Proxytrace.Domain/ModelEndpoint/Internal/ModelEndpointGenerator.cs b/Proxytrace.Domain/ModelEndpoint/Internal/ModelEndpointGenerator.cs index 0372a8183..bc5b56c43 100644 --- a/Proxytrace.Domain/ModelEndpoint/Internal/ModelEndpointGenerator.cs +++ b/Proxytrace.Domain/ModelEndpoint/Internal/ModelEndpointGenerator.cs @@ -11,6 +11,9 @@ internal class ModelEndpointGenerator : DomainEntityGenerator private readonly IDomainEntityGenerator modelGenerator; private readonly IDomainEntityGenerator providerGenerator; + /// + /// Initializes a new instance of the class. + /// public ModelEndpointGenerator( IModelEndpoint.CreateNew factory, IRepository repository, @@ -23,6 +26,9 @@ public ModelEndpointGenerator( this.providerGenerator = providerGenerator; } + /// + /// Generates asynchronously. + /// public override async Task GenerateAsync(CancellationToken cancellationToken = default) { var model = await modelGenerator.GetOrCreateAsync(cancellationToken); diff --git a/Proxytrace.Domain/ModelProvider/IProviderClient.cs b/Proxytrace.Domain/ModelProvider/IProviderClient.cs index ebbdf3756..ae19baec0 100644 --- a/Proxytrace.Domain/ModelProvider/IProviderClient.cs +++ b/Proxytrace.Domain/ModelProvider/IProviderClient.cs @@ -1,7 +1,13 @@ namespace Proxytrace.Domain.ModelProvider; +/// +/// Client for communicating with the provider endpoint. +/// public interface IProviderClient { + /// + /// Encapsulates a factory operation. + /// public delegate IProviderClient Factory(IModelProvider provider); Task VerifyConnectionAsync(CancellationToken cancellationToken = default); diff --git a/Proxytrace.Domain/ModelProvider/Internal/ModelProvider.cs b/Proxytrace.Domain/ModelProvider/Internal/ModelProvider.cs index 205aba6ad..4b7b03f22 100644 --- a/Proxytrace.Domain/ModelProvider/Internal/ModelProvider.cs +++ b/Proxytrace.Domain/ModelProvider/Internal/ModelProvider.cs @@ -8,11 +8,26 @@ namespace Proxytrace.Domain.ModelProvider.Internal; internal record ModelProvider : DomainEntity, IModelProvider { private readonly IProviderClient.Factory clientFactory; + /// + /// Gets the name. + /// public string Name { get; } + /// + /// Gets the endpoint. + /// public Uri Endpoint { get; } + /// + /// Gets the api key. + /// public string ApiKey { get; } + /// + /// Gets the kind. + /// public ModelProviderKind Kind { get; } + /// + /// Initializes a new instance of the class. + /// public ModelProvider( string name, Uri endpoint, @@ -28,6 +43,9 @@ public ModelProvider( Kind = kind; } + /// + /// Initializes a new instance of the class. + /// public ModelProvider( string name, Uri endpoint, @@ -44,6 +62,9 @@ public ModelProvider( Kind = kind; } + /// + /// Creates the client. + /// public IProviderClient CreateClient() => clientFactory(this); @@ -64,6 +85,9 @@ protected override bool PrintMembers(StringBuilder builder) return true; } + /// + /// Validates. + /// public override IEnumerable Validate(ValidationContext validationContext) { foreach (var result in base.Validate(validationContext)) diff --git a/Proxytrace.Domain/ModelProvider/Internal/ModelProviderGenerator.cs b/Proxytrace.Domain/ModelProvider/Internal/ModelProviderGenerator.cs index 78cabf8fb..d28d548ec 100644 --- a/Proxytrace.Domain/ModelProvider/Internal/ModelProviderGenerator.cs +++ b/Proxytrace.Domain/ModelProvider/Internal/ModelProviderGenerator.cs @@ -15,6 +15,9 @@ internal class ModelProviderGenerator : DomainEntityGenerator private readonly IModelProvider.CreateNew factory; + /// + /// Initializes a new instance of the class. + /// public ModelProviderGenerator( IModelProvider.CreateNew factory, IRepository repository, @@ -23,6 +26,9 @@ public ModelProviderGenerator( this.factory = factory; } + /// + /// Generates asynchronously. + /// public override Task GenerateAsync(CancellationToken cancellationToken = default) => Task.FromResult(factory( name: $"{random.Any(ProviderNames)}-{random.UniqueString()}", diff --git a/Proxytrace.Domain/ModelProvider/ModelPrice.cs b/Proxytrace.Domain/ModelProvider/ModelPrice.cs index a0f70d71c..fe5f1f7d8 100644 --- a/Proxytrace.Domain/ModelProvider/ModelPrice.cs +++ b/Proxytrace.Domain/ModelProvider/ModelPrice.cs @@ -3,5 +3,8 @@ namespace Proxytrace.Domain.ModelProvider; /// Resolved per-model price in EUR per 1M tokens; nulls when unresolved. public record ModelPrice(decimal? InputTokenCost, decimal? OutputTokenCost, decimal? CachedInputTokenCost = null) { + /// + /// The unknown. + /// public static readonly ModelPrice Unknown = new(null, null, null); } diff --git a/Proxytrace.Domain/ModelProvider/ProviderConnectionError.cs b/Proxytrace.Domain/ModelProvider/ProviderConnectionError.cs index 28ddae16c..5db436c23 100644 --- a/Proxytrace.Domain/ModelProvider/ProviderConnectionError.cs +++ b/Proxytrace.Domain/ModelProvider/ProviderConnectionError.cs @@ -1,5 +1,8 @@ namespace Proxytrace.Domain.ModelProvider; +/// +/// Specifies the provider connection error. +/// public enum ProviderConnectionError { Unauthorized, diff --git a/Proxytrace.Domain/ModelProvider/ProviderConnectionException.cs b/Proxytrace.Domain/ModelProvider/ProviderConnectionException.cs index bd9048425..10f49e28c 100644 --- a/Proxytrace.Domain/ModelProvider/ProviderConnectionException.cs +++ b/Proxytrace.Domain/ModelProvider/ProviderConnectionException.cs @@ -1,12 +1,21 @@ namespace Proxytrace.Domain.ModelProvider; +/// +/// The exception that is thrown when a provider connection error occurs. +/// public sealed class ProviderConnectionException : Exception { + /// + /// Initializes a new instance of the class. + /// public ProviderConnectionException(ProviderConnectionError error, Exception innerException) : base($"Provider connection failed: {error}", innerException) { Error = error; } + /// + /// Gets the error. + /// public ProviderConnectionError Error { get; } } diff --git a/Proxytrace.Domain/ModelProvider/ProviderConnectionResult.cs b/Proxytrace.Domain/ModelProvider/ProviderConnectionResult.cs index a2dc27bcd..2e76aa108 100644 --- a/Proxytrace.Domain/ModelProvider/ProviderConnectionResult.cs +++ b/Proxytrace.Domain/ModelProvider/ProviderConnectionResult.cs @@ -1,5 +1,8 @@ namespace Proxytrace.Domain.ModelProvider; +/// +/// Encapsulates the result of a provider connection operation. +/// public record ProviderConnectionResult( bool Success, ProviderConnectionError? Error, diff --git a/Proxytrace.Domain/Module.cs b/Proxytrace.Domain/Module.cs index 3b5019a02..5e0b2cebb 100644 --- a/Proxytrace.Domain/Module.cs +++ b/Proxytrace.Domain/Module.cs @@ -16,6 +16,9 @@ namespace Proxytrace.Domain; +/// +/// Autofac module that registers services. +/// public sealed class Module : Autofac.Module { private const string RegisteredKey = "Proxytrace.Domain.Module.Registered"; diff --git a/Proxytrace.Domain/Notification/INotification.cs b/Proxytrace.Domain/Notification/INotification.cs index 0bd5bb816..48c36e064 100644 --- a/Proxytrace.Domain/Notification/INotification.cs +++ b/Proxytrace.Domain/Notification/INotification.cs @@ -49,6 +49,9 @@ public interface INotification : IDomainEntity /// Task Dismiss(CancellationToken cancellationToken = default); + /// + /// Factory delegate for creating a new new instance. + /// public delegate INotification CreateNew( NotificationKind kind, NotificationSeverity severity, @@ -58,6 +61,9 @@ public delegate INotification CreateNew( NotificationTargetKind? targetKind, Guid? targetId); + /// + /// Factory delegate for creating a new existing instance. + /// public delegate INotification CreateExisting( NotificationKind kind, NotificationSeverity severity, diff --git a/Proxytrace.Domain/Notification/Internal/Notification.cs b/Proxytrace.Domain/Notification/Internal/Notification.cs index 6fe071f0e..b13736692 100644 --- a/Proxytrace.Domain/Notification/Internal/Notification.cs +++ b/Proxytrace.Domain/Notification/Internal/Notification.cs @@ -6,15 +6,42 @@ namespace Proxytrace.Domain.Notification.Internal; internal record Notification : DomainEntity, INotification { + /// + /// Gets the kind. + /// public NotificationKind Kind { get; } + /// + /// Gets the severity. + /// public NotificationSeverity Severity { get; } + /// + /// Gets the title. + /// public string Title { get; } + /// + /// Gets the message. + /// public string Message { get; } + /// + /// Gets or sets the status. + /// public NotificationStatus Status { get; private init; } + /// + /// Gets the project id. + /// public Guid? ProjectId { get; } + /// + /// Gets the target kind. + /// public NotificationTargetKind? TargetKind { get; } + /// + /// Gets the target id. + /// public Guid? TargetId { get; } + /// + /// Initializes a new instance of the class. + /// public Notification( NotificationKind kind, NotificationSeverity severity, @@ -35,6 +62,9 @@ public Notification( TargetId = targetId; } + /// + /// Initializes a new instance of the class. + /// public Notification( NotificationKind kind, NotificationSeverity severity, @@ -57,6 +87,9 @@ public Notification( TargetId = targetId; } + /// + /// Mark read. + /// public Task MarkRead(CancellationToken cancellationToken = default) { if (Status == NotificationStatus.Read) @@ -68,6 +101,9 @@ public Task MarkRead(CancellationToken cancellationToken = defaul return ApplyAsync(this with { Status = NotificationStatus.Read }, cancellationToken); } + /// + /// Dismiss. + /// public Task Dismiss(CancellationToken cancellationToken = default) { if (Status == NotificationStatus.Dismissed) @@ -76,6 +112,9 @@ public Task Dismiss(CancellationToken cancellationToken = default return ApplyAsync(this with { Status = NotificationStatus.Dismissed }, cancellationToken); } + /// + /// Validates. + /// public override IEnumerable Validate(ValidationContext validationContext) { foreach (var result in base.Validate(validationContext)) diff --git a/Proxytrace.Domain/Notification/Internal/NotificationGenerator.cs b/Proxytrace.Domain/Notification/Internal/NotificationGenerator.cs index 35192f34a..f879f3c9c 100644 --- a/Proxytrace.Domain/Notification/Internal/NotificationGenerator.cs +++ b/Proxytrace.Domain/Notification/Internal/NotificationGenerator.cs @@ -7,6 +7,9 @@ internal class NotificationGenerator : DomainEntityGenerator { private readonly INotification.CreateNew factory; + /// + /// Initializes a new instance of the class. + /// public NotificationGenerator( INotification.CreateNew factory, IRepository repository, @@ -15,6 +18,9 @@ public NotificationGenerator( this.factory = factory; } + /// + /// Generates asynchronously. + /// public override Task GenerateAsync(CancellationToken cancellationToken = default) => Task.FromResult(factory( kind: random.Enum(), diff --git a/Proxytrace.Domain/OptimizationProposal/IModelSwitchProposal.cs b/Proxytrace.Domain/OptimizationProposal/IModelSwitchProposal.cs index 4b8ad2b63..a52d08e02 100644 --- a/Proxytrace.Domain/OptimizationProposal/IModelSwitchProposal.cs +++ b/Proxytrace.Domain/OptimizationProposal/IModelSwitchProposal.cs @@ -19,6 +19,9 @@ public interface IModelSwitchProposal : IOptimizationProposal /// Observed latency delta (proposed - current) from the evidence runs. TimeSpan? ExpectedLatencyDelta { get; } + /// + /// Factory delegate for creating a new new instance. + /// public delegate IModelSwitchProposal CreateNew( IAgent agent, Priority priority, @@ -31,6 +34,9 @@ public delegate IModelSwitchProposal CreateNew( IReadOnlyCollection evidenceTestRunIds, ITestRun abTestRun); + /// + /// Factory delegate for creating a new existing instance. + /// public delegate IModelSwitchProposal CreateExisting( IAgent agent, ProposalStatus status, diff --git a/Proxytrace.Domain/OptimizationProposal/ISystemPromptProposal.cs b/Proxytrace.Domain/OptimizationProposal/ISystemPromptProposal.cs index 143d9b95a..f224abd72 100644 --- a/Proxytrace.Domain/OptimizationProposal/ISystemPromptProposal.cs +++ b/Proxytrace.Domain/OptimizationProposal/ISystemPromptProposal.cs @@ -12,6 +12,9 @@ public interface ISystemPromptProposal : IOptimizationProposal /// The full proposed system prompt text. string ProposedSystemMessage { get; } + /// + /// Factory delegate for creating a new new instance. + /// public delegate ISystemPromptProposal CreateNew( IAgent agent, Priority priority, @@ -22,6 +25,9 @@ public delegate ISystemPromptProposal CreateNew( IReadOnlyCollection evidenceTestRunIds, ITestRun abTestRun); + /// + /// Factory delegate for creating a new existing instance. + /// public delegate ISystemPromptProposal CreateExisting( IAgent agent, ProposalStatus status, diff --git a/Proxytrace.Domain/OptimizationProposal/IToolUpdateProposal.cs b/Proxytrace.Domain/OptimizationProposal/IToolUpdateProposal.cs index 5b5ea67e9..421ca6fc3 100644 --- a/Proxytrace.Domain/OptimizationProposal/IToolUpdateProposal.cs +++ b/Proxytrace.Domain/OptimizationProposal/IToolUpdateProposal.cs @@ -13,6 +13,9 @@ public interface IToolUpdateProposal : IOptimizationProposal /// The proposed tool specifications, replacing the agent's current ones. IReadOnlyList ProposedTools { get; } + /// + /// Factory delegate for creating a new new instance. + /// public delegate IToolUpdateProposal CreateNew( IAgent agent, Priority priority, @@ -23,6 +26,9 @@ public delegate IToolUpdateProposal CreateNew( IReadOnlyCollection evidenceTestRunIds, ITestRun abTestRun); + /// + /// Factory delegate for creating a new existing instance. + /// public delegate IToolUpdateProposal CreateExisting( IAgent agent, ProposalStatus status, diff --git a/Proxytrace.Domain/OptimizationProposal/Internal/ModelSwitchProposal.cs b/Proxytrace.Domain/OptimizationProposal/Internal/ModelSwitchProposal.cs index c30c3102e..4b02fc83d 100644 --- a/Proxytrace.Domain/OptimizationProposal/Internal/ModelSwitchProposal.cs +++ b/Proxytrace.Domain/OptimizationProposal/Internal/ModelSwitchProposal.cs @@ -11,11 +11,26 @@ namespace Proxytrace.Domain.OptimizationProposal.Internal; [UsedImplicitly] internal record ModelSwitchProposal : OptimizationProposal, IModelSwitchProposal { + /// + /// Gets the kind. + /// public override ProposalKind Kind => ProposalKind.ModelSwitch; + /// + /// Gets or sets the proposed endpoint. + /// public IModelEndpoint ProposedEndpoint { get; private init; } + /// + /// Gets or sets the expected cost delta. + /// public decimal? ExpectedCostDelta { get; private init; } + /// + /// Gets or sets the expected latency delta. + /// public TimeSpan? ExpectedLatencyDelta { get; private init; } + /// + /// Initializes a new instance of the class. + /// public ModelSwitchProposal( IAgent agent, Priority priority, @@ -37,6 +52,9 @@ public ModelSwitchProposal( ExpectedLatencyDelta = expectedLatencyDelta; } + /// + /// Initializes a new instance of the class. + /// public ModelSwitchProposal( IAgent agent, ProposalStatus status, @@ -64,6 +82,9 @@ public ModelSwitchProposal( ExpectedLatencyDelta = expectedLatencyDelta; } + /// + /// Validates. + /// public override IEnumerable Validate(ValidationContext validationContext) { foreach (var result in base.Validate(validationContext)) diff --git a/Proxytrace.Domain/OptimizationProposal/Internal/ModelSwitchProposalGenerator.cs b/Proxytrace.Domain/OptimizationProposal/Internal/ModelSwitchProposalGenerator.cs index d8428082b..eb2746a14 100644 --- a/Proxytrace.Domain/OptimizationProposal/Internal/ModelSwitchProposalGenerator.cs +++ b/Proxytrace.Domain/OptimizationProposal/Internal/ModelSwitchProposalGenerator.cs @@ -14,6 +14,9 @@ internal class ModelSwitchProposalGenerator : OptimizationProposalGeneratorBase< private readonly IDomainEntityGenerator testRunGenerator; private readonly IRandom random; + /// + /// Initializes a new instance of the class. + /// public ModelSwitchProposalGenerator( IModelSwitchProposal.CreateNew factory, IDomainEntityGenerator agentGenerator, @@ -29,6 +32,9 @@ public ModelSwitchProposalGenerator( this.random = random; } + /// + /// Generates asynchronously. + /// public override async Task GenerateAsync(CancellationToken cancellationToken = default) { var agent = await agentGenerator.GetOrCreateAsync(cancellationToken); diff --git a/Proxytrace.Domain/OptimizationProposal/Internal/OptimizationProposal.cs b/Proxytrace.Domain/OptimizationProposal/Internal/OptimizationProposal.cs index 2db7d10a4..a595eba1a 100644 --- a/Proxytrace.Domain/OptimizationProposal/Internal/OptimizationProposal.cs +++ b/Proxytrace.Domain/OptimizationProposal/Internal/OptimizationProposal.cs @@ -15,19 +15,61 @@ namespace Proxytrace.Domain.OptimizationProposal.Internal; /// internal abstract record OptimizationProposal : DomainEntity, IOptimizationProposal { + /// + /// Gets or sets the agent. + /// public IAgent Agent { get; private init; } + /// + /// Gets the kind. + /// public abstract ProposalKind Kind { get; } + /// + /// Gets or sets the status. + /// public ProposalStatus Status { get; private init; } + /// + /// Gets or sets the priority. + /// public Priority Priority { get; private init; } + /// + /// Gets or sets the rationale. + /// public string Rationale { get; private init; } + /// + /// Gets or sets the ab test run. + /// public ITestRun ABTestRun { get; private init; } + /// + /// Gets or sets the current pass rate. + /// public double? CurrentPassRate { get; private init; } + /// + /// Gets or sets the proposed pass rate. + /// public double? ProposedPassRate { get; private init; } + /// + /// Gets or sets the evidence test run ids. + /// public IReadOnlyCollection EvidenceTestRunIds { get; private init; } + /// + /// Gets or sets the content hash. + /// public string ContentHash { get; private init; } + /// + /// Gets or sets the adopted at. + /// public DateTimeOffset? AdoptedAt { get; private init; } + /// + /// Gets or sets the adopted agent version id. + /// public Guid? AdoptedAgentVersionId { get; private init; } + /// + /// Gets or sets the adopted agent version number. + /// public int? AdoptedAgentVersionNumber { get; private init; } + /// + /// Gets or sets the adopted manually. + /// public bool? AdoptedManually { get; private init; } protected OptimizationProposal( @@ -84,6 +126,9 @@ protected OptimizationProposal( AdoptedManually = adoptedManually; } + /// + /// Accepts. + /// public Task Accept(CancellationToken cancellationToken = default) { if (Status != ProposalStatus.Draft) @@ -92,6 +137,9 @@ public Task Accept(CancellationToken cancellationToken = return ApplyAsync(this with { Status = ProposalStatus.Accepted }, cancellationToken); } + /// + /// Rejects. + /// public Task Reject(CancellationToken cancellationToken = default) { if (Status != ProposalStatus.Draft) @@ -100,6 +148,9 @@ public Task Reject(CancellationToken cancellationToken = return ApplyAsync(this with { Status = ProposalStatus.Rejected }, cancellationToken); } + /// + /// Mark adopted. + /// public Task MarkAdopted( IAgentVersion? adoptedVersion, bool manual, @@ -120,6 +171,9 @@ this with cancellationToken); } + /// + /// Validates. + /// public override IEnumerable Validate(ValidationContext validationContext) { foreach (var result in base.Validate(validationContext)) diff --git a/Proxytrace.Domain/OptimizationProposal/Internal/OptimizationProposalGenerator.cs b/Proxytrace.Domain/OptimizationProposal/Internal/OptimizationProposalGenerator.cs index 1720f3bbd..5d88e5a2c 100644 --- a/Proxytrace.Domain/OptimizationProposal/Internal/OptimizationProposalGenerator.cs +++ b/Proxytrace.Domain/OptimizationProposal/Internal/OptimizationProposalGenerator.cs @@ -9,6 +9,9 @@ internal class OptimizationProposalGenerator : DomainEntityGenerator toolUpdateGenerator; private readonly IDomainEntityGenerator modelSwitchGenerator; + /// + /// Initializes a new instance of the class. + /// public OptimizationProposalGenerator( IRepository repository, IDomainEntityGenerator systemPromptGenerator, @@ -21,9 +24,15 @@ public OptimizationProposalGenerator( this.modelSwitchGenerator = modelSwitchGenerator; } + /// + /// Generates asynchronously. + /// public override async Task GenerateAsync(CancellationToken cancellationToken = default) => await systemPromptGenerator.GenerateAsync(cancellationToken); + /// + /// Creates asynchronously. + /// public async Task CreateAsync(ProposalKind kind, CancellationToken cancellationToken = default) => kind switch { diff --git a/Proxytrace.Domain/OptimizationProposal/Internal/OptimizationProposalGeneratorBase.cs b/Proxytrace.Domain/OptimizationProposal/Internal/OptimizationProposalGeneratorBase.cs index 9a29bca68..849c15838 100644 --- a/Proxytrace.Domain/OptimizationProposal/Internal/OptimizationProposalGeneratorBase.cs +++ b/Proxytrace.Domain/OptimizationProposal/Internal/OptimizationProposalGeneratorBase.cs @@ -10,14 +10,23 @@ protected OptimizationProposalGeneratorBase(IRepository r this.repository = repository; } + /// + /// Generates asynchronously. + /// public abstract Task GenerateAsync(CancellationToken cancellationToken = default); + /// + /// Creates asynchronously. + /// public async Task CreateAsync(CancellationToken cancellationToken = default) { var instance = await GenerateAsync(cancellationToken); return (T)await repository.AddAsync(instance, cancellationToken); } + /// + /// Gets the or create asynchronously. + /// public async Task GetOrCreateAsync(CancellationToken cancellationToken = default) { var existing = await repository.FindFirstAsync(cancellationToken); diff --git a/Proxytrace.Domain/OptimizationProposal/Internal/SystemPromptProposal.cs b/Proxytrace.Domain/OptimizationProposal/Internal/SystemPromptProposal.cs index c9f9a4984..0d049040d 100644 --- a/Proxytrace.Domain/OptimizationProposal/Internal/SystemPromptProposal.cs +++ b/Proxytrace.Domain/OptimizationProposal/Internal/SystemPromptProposal.cs @@ -11,9 +11,18 @@ namespace Proxytrace.Domain.OptimizationProposal.Internal; [UsedImplicitly] internal record SystemPromptProposal : OptimizationProposal, ISystemPromptProposal { + /// + /// Gets the kind. + /// public override ProposalKind Kind => ProposalKind.SystemPrompt; + /// + /// Gets or sets the proposed system message. + /// public string ProposedSystemMessage { get; private init; } + /// + /// Initializes a new instance of the class. + /// public SystemPromptProposal( IAgent agent, Priority priority, @@ -31,6 +40,9 @@ public SystemPromptProposal( ProposedSystemMessage = proposedSystemMessage; } + /// + /// Initializes a new instance of the class. + /// public SystemPromptProposal( IAgent agent, ProposalStatus status, @@ -54,6 +66,9 @@ public SystemPromptProposal( ProposedSystemMessage = proposedSystemMessage; } + /// + /// Validates. + /// public override IEnumerable Validate(ValidationContext validationContext) { foreach (var result in base.Validate(validationContext)) diff --git a/Proxytrace.Domain/OptimizationProposal/Internal/SystemPromptProposalGenerator.cs b/Proxytrace.Domain/OptimizationProposal/Internal/SystemPromptProposalGenerator.cs index d74124bec..0c6f2725a 100644 --- a/Proxytrace.Domain/OptimizationProposal/Internal/SystemPromptProposalGenerator.cs +++ b/Proxytrace.Domain/OptimizationProposal/Internal/SystemPromptProposalGenerator.cs @@ -12,6 +12,9 @@ internal class SystemPromptProposalGenerator : OptimizationProposalGeneratorBase private readonly IDomainEntityGenerator testRunGenerator; private readonly IRandom random; + /// + /// Initializes a new instance of the class. + /// public SystemPromptProposalGenerator( ISystemPromptProposal.CreateNew factory, IDomainEntityGenerator agentGenerator, @@ -25,6 +28,9 @@ public SystemPromptProposalGenerator( this.random = random; } + /// + /// Generates asynchronously. + /// public override async Task GenerateAsync(CancellationToken cancellationToken = default) { var agent = await agentGenerator.GetOrCreateAsync(cancellationToken); diff --git a/Proxytrace.Domain/OptimizationProposal/Internal/ToolUpdateProposal.cs b/Proxytrace.Domain/OptimizationProposal/Internal/ToolUpdateProposal.cs index d1ae734fc..87d748f60 100644 --- a/Proxytrace.Domain/OptimizationProposal/Internal/ToolUpdateProposal.cs +++ b/Proxytrace.Domain/OptimizationProposal/Internal/ToolUpdateProposal.cs @@ -11,9 +11,18 @@ namespace Proxytrace.Domain.OptimizationProposal.Internal; [UsedImplicitly] internal record ToolUpdateProposal : OptimizationProposal, IToolUpdateProposal { + /// + /// Gets the kind. + /// public override ProposalKind Kind => ProposalKind.Tool; + /// + /// Gets or sets the proposed tools. + /// public IReadOnlyList ProposedTools { get; private init; } + /// + /// Initializes a new instance of the class. + /// public ToolUpdateProposal( IAgent agent, Priority priority, @@ -31,6 +40,9 @@ public ToolUpdateProposal( ProposedTools = proposedTools.ToArray(); } + /// + /// Initializes a new instance of the class. + /// public ToolUpdateProposal( IAgent agent, ProposalStatus status, @@ -54,6 +66,9 @@ public ToolUpdateProposal( ProposedTools = proposedTools.ToArray(); } + /// + /// Validates. + /// public override IEnumerable Validate(ValidationContext validationContext) { foreach (var result in base.Validate(validationContext)) diff --git a/Proxytrace.Domain/OptimizationProposal/Internal/ToolUpdateProposalGenerator.cs b/Proxytrace.Domain/OptimizationProposal/Internal/ToolUpdateProposalGenerator.cs index a5212f925..274eef2ae 100644 --- a/Proxytrace.Domain/OptimizationProposal/Internal/ToolUpdateProposalGenerator.cs +++ b/Proxytrace.Domain/OptimizationProposal/Internal/ToolUpdateProposalGenerator.cs @@ -12,6 +12,9 @@ internal class ToolUpdateProposalGenerator : OptimizationProposalGeneratorBase testRunGenerator; private readonly IRandom random; + /// + /// Initializes a new instance of the class. + /// public ToolUpdateProposalGenerator( IToolUpdateProposal.CreateNew factory, IDomainEntityGenerator agentGenerator, @@ -25,6 +28,9 @@ public ToolUpdateProposalGenerator( this.random = random; } + /// + /// Generates asynchronously. + /// public override async Task GenerateAsync(CancellationToken cancellationToken = default) { var agent = await agentGenerator.GetOrCreateAsync(cancellationToken); diff --git a/Proxytrace.Domain/OptimizationTheory/IModelSwitchTheory.cs b/Proxytrace.Domain/OptimizationTheory/IModelSwitchTheory.cs index b8a494407..8138c1295 100644 --- a/Proxytrace.Domain/OptimizationTheory/IModelSwitchTheory.cs +++ b/Proxytrace.Domain/OptimizationTheory/IModelSwitchTheory.cs @@ -13,6 +13,9 @@ public interface IModelSwitchTheory : IOptimizationTheory /// The endpoint proposed as a replacement for the agent's current one. IModelEndpoint ProposedEndpoint { get; } + /// + /// Factory delegate for creating a new new instance. + /// public delegate IModelSwitchTheory CreateNew( IAgent agent, ITestSuite suite, @@ -22,6 +25,9 @@ public delegate IModelSwitchTheory CreateNew( IModelEndpoint proposedEndpoint, IReadOnlyCollection evidenceTestRunIds); + /// + /// Factory delegate for creating a new existing instance. + /// public delegate IModelSwitchTheory CreateExisting( IAgent agent, ITestSuite suite, diff --git a/Proxytrace.Domain/OptimizationTheory/ISystemPromptTheory.cs b/Proxytrace.Domain/OptimizationTheory/ISystemPromptTheory.cs index 55cc98395..15a10c31a 100644 --- a/Proxytrace.Domain/OptimizationTheory/ISystemPromptTheory.cs +++ b/Proxytrace.Domain/OptimizationTheory/ISystemPromptTheory.cs @@ -12,6 +12,9 @@ public interface ISystemPromptTheory : IOptimizationTheory /// The full proposed system prompt text. string ProposedSystemMessage { get; } + /// + /// Factory delegate for creating a new new instance. + /// public delegate ISystemPromptTheory CreateNew( IAgent agent, ITestSuite suite, @@ -21,6 +24,9 @@ public delegate ISystemPromptTheory CreateNew( string proposedSystemMessage, IReadOnlyCollection evidenceTestRunIds); + /// + /// Factory delegate for creating a new existing instance. + /// public delegate ISystemPromptTheory CreateExisting( IAgent agent, ITestSuite suite, diff --git a/Proxytrace.Domain/OptimizationTheory/IToolUpdateTheory.cs b/Proxytrace.Domain/OptimizationTheory/IToolUpdateTheory.cs index 5f1daba99..cdd76fac7 100644 --- a/Proxytrace.Domain/OptimizationTheory/IToolUpdateTheory.cs +++ b/Proxytrace.Domain/OptimizationTheory/IToolUpdateTheory.cs @@ -13,6 +13,9 @@ public interface IToolUpdateTheory : IOptimizationTheory /// The proposed tool specifications, replacing the agent's current ones. IReadOnlyList ProposedTools { get; } + /// + /// Factory delegate for creating a new new instance. + /// public delegate IToolUpdateTheory CreateNew( IAgent agent, ITestSuite suite, @@ -22,6 +25,9 @@ public delegate IToolUpdateTheory CreateNew( IReadOnlyList proposedTools, IReadOnlyCollection evidenceTestRunIds); + /// + /// Factory delegate for creating a new existing instance. + /// public delegate IToolUpdateTheory CreateExisting( IAgent agent, ITestSuite suite, diff --git a/Proxytrace.Domain/OptimizationTheory/Internal/ModelSwitchTheory.cs b/Proxytrace.Domain/OptimizationTheory/Internal/ModelSwitchTheory.cs index e2a76688b..65e3507d2 100644 --- a/Proxytrace.Domain/OptimizationTheory/Internal/ModelSwitchTheory.cs +++ b/Proxytrace.Domain/OptimizationTheory/Internal/ModelSwitchTheory.cs @@ -13,9 +13,18 @@ namespace Proxytrace.Domain.OptimizationTheory.Internal; [UsedImplicitly] internal record ModelSwitchTheory : OptimizationTheory, IModelSwitchTheory { + /// + /// Gets the kind. + /// public override ProposalKind Kind => ProposalKind.ModelSwitch; + /// + /// Gets or sets the proposed endpoint. + /// public IModelEndpoint ProposedEndpoint { get; private init; } + /// + /// Initializes a new instance of the class. + /// public ModelSwitchTheory( IAgent agent, ITestSuite suite, @@ -32,6 +41,9 @@ public ModelSwitchTheory( ProposedEndpoint = proposedEndpoint; } + /// + /// Initializes a new instance of the class. + /// public ModelSwitchTheory( IAgent agent, ITestSuite suite, @@ -55,6 +67,9 @@ public ModelSwitchTheory( ProposedEndpoint = proposedEndpoint; } + /// + /// Validates. + /// public override IEnumerable Validate(ValidationContext validationContext) { foreach (var result in base.Validate(validationContext)) diff --git a/Proxytrace.Domain/OptimizationTheory/Internal/ModelSwitchTheoryGenerator.cs b/Proxytrace.Domain/OptimizationTheory/Internal/ModelSwitchTheoryGenerator.cs index f24d06e54..4ee094093 100644 --- a/Proxytrace.Domain/OptimizationTheory/Internal/ModelSwitchTheoryGenerator.cs +++ b/Proxytrace.Domain/OptimizationTheory/Internal/ModelSwitchTheoryGenerator.cs @@ -14,6 +14,9 @@ internal class ModelSwitchTheoryGenerator : OptimizationTheoryGeneratorBase endpointGenerator; private readonly IRandom random; + /// + /// Initializes a new instance of the class. + /// public ModelSwitchTheoryGenerator( IModelSwitchTheory.CreateNew factory, IDomainEntityGenerator agentGenerator, @@ -29,6 +32,9 @@ public ModelSwitchTheoryGenerator( this.random = random; } + /// + /// Generates asynchronously. + /// public override async Task GenerateAsync(CancellationToken cancellationToken = default) { var agent = await agentGenerator.GetOrCreateAsync(cancellationToken); diff --git a/Proxytrace.Domain/OptimizationTheory/Internal/OptimizationTheory.cs b/Proxytrace.Domain/OptimizationTheory/Internal/OptimizationTheory.cs index 6ceb31cbc..4d6ef7195 100644 --- a/Proxytrace.Domain/OptimizationTheory/Internal/OptimizationTheory.cs +++ b/Proxytrace.Domain/OptimizationTheory/Internal/OptimizationTheory.cs @@ -14,19 +14,61 @@ namespace Proxytrace.Domain.OptimizationTheory.Internal; /// internal abstract record OptimizationTheory : DomainEntity, IOptimizationTheory { + /// + /// Gets or sets the agent. + /// public IAgent Agent { get; private init; } + /// + /// Gets or sets the suite. + /// public ITestSuite Suite { get; private init; } + /// + /// Gets the kind. + /// public abstract ProposalKind Kind { get; } + /// + /// Gets or sets the status. + /// public TheoryStatus Status { get; private init; } + /// + /// Gets or sets the source. + /// public TheorySource Source { get; private init; } + /// + /// Gets or sets the priority. + /// public Priority Priority { get; private init; } + /// + /// Gets or sets the rationale. + /// public string Rationale { get; private init; } + /// + /// Gets or sets the evidence test run ids. + /// public IReadOnlyCollection EvidenceTestRunIds { get; private init; } + /// + /// Gets or sets the resulting proposal id. + /// public Guid? ResultingProposalId { get; private init; } + /// + /// Gets or sets the baseline pass rate. + /// public double? BaselinePassRate { get; private init; } + /// + /// Gets or sets the projected pass rate. + /// public double? ProjectedPassRate { get; private init; } + /// + /// Gets or sets the p value. + /// public double? PValue { get; private init; } + /// + /// Gets or sets the ab test run id. + /// public Guid? ABTestRunId { get; private init; } + /// + /// Gets or sets the content hash. + /// public string ContentHash { get; private init; } protected OptimizationTheory( @@ -82,6 +124,9 @@ protected OptimizationTheory( ContentHash = contentHash; } + /// + /// Sets the validating. + /// public Task SetValidating(CancellationToken cancellationToken = default) { if (Status != TheoryStatus.Proposed) @@ -90,6 +135,9 @@ public Task SetValidating(CancellationToken cancellationTok return ApplyAsync(this with { Status = TheoryStatus.Validating }, cancellationToken); } + /// + /// Attaches the ab test run. + /// public Task AttachAbTestRun(Guid abTestRunId, CancellationToken cancellationToken = default) { if (Status != TheoryStatus.Validating) @@ -98,6 +146,9 @@ public Task AttachAbTestRun(Guid abTestRunId, CancellationT return ApplyAsync(this with { ABTestRunId = abTestRunId }, cancellationToken); } + /// + /// Sets the validated. + /// public Task SetValidated( Guid resultingProposalId, double? baselinePassRate, @@ -122,6 +173,9 @@ this with cancellationToken); } + /// + /// Sets the invalidated. + /// public Task SetInvalidated( double? baselinePassRate, double? projectedPassRate, @@ -144,6 +198,9 @@ this with cancellationToken); } + /// + /// Sets the failed. + /// public Task SetFailed(Guid? abTestRunId, CancellationToken cancellationToken = default) { if (Status != TheoryStatus.Validating) @@ -154,6 +211,9 @@ public Task SetFailed(Guid? abTestRunId, CancellationToken return ApplyAsync(this with { Status = TheoryStatus.Failed, ABTestRunId = abTestRunId }, cancellationToken); } + /// + /// Rejects. + /// public Task Reject(CancellationToken cancellationToken = default) { if (Status is not (TheoryStatus.Proposed or TheoryStatus.Validating or TheoryStatus.Failed)) @@ -164,6 +224,9 @@ public Task Reject(CancellationToken cancellationToken = de return ApplyAsync(this with { Status = TheoryStatus.Invalidated }, cancellationToken); } + /// + /// Reset to proposed. + /// public Task ResetToProposed(CancellationToken cancellationToken = default) { if (Status is not (TheoryStatus.Validated or TheoryStatus.Invalidated or TheoryStatus.Failed)) @@ -182,6 +245,9 @@ this with cancellationToken); } + /// + /// Validates. + /// public override IEnumerable Validate(ValidationContext validationContext) { foreach (var result in base.Validate(validationContext)) diff --git a/Proxytrace.Domain/OptimizationTheory/Internal/OptimizationTheoryGenerator.cs b/Proxytrace.Domain/OptimizationTheory/Internal/OptimizationTheoryGenerator.cs index d281503fa..18370c834 100644 --- a/Proxytrace.Domain/OptimizationTheory/Internal/OptimizationTheoryGenerator.cs +++ b/Proxytrace.Domain/OptimizationTheory/Internal/OptimizationTheoryGenerator.cs @@ -10,6 +10,9 @@ internal class OptimizationTheoryGenerator : DomainEntityGenerator toolUpdateGenerator; private readonly IDomainEntityGenerator modelSwitchGenerator; + /// + /// Initializes a new instance of the class. + /// public OptimizationTheoryGenerator( IRepository repository, IDomainEntityGenerator systemPromptGenerator, @@ -22,9 +25,15 @@ public OptimizationTheoryGenerator( this.modelSwitchGenerator = modelSwitchGenerator; } + /// + /// Generates asynchronously. + /// public override async Task GenerateAsync(CancellationToken cancellationToken = default) => await systemPromptGenerator.GenerateAsync(cancellationToken); + /// + /// Creates asynchronously. + /// public async Task CreateAsync(ProposalKind kind, CancellationToken cancellationToken = default) => kind switch { diff --git a/Proxytrace.Domain/OptimizationTheory/Internal/OptimizationTheoryGeneratorBase.cs b/Proxytrace.Domain/OptimizationTheory/Internal/OptimizationTheoryGeneratorBase.cs index fa614256e..4110afc9b 100644 --- a/Proxytrace.Domain/OptimizationTheory/Internal/OptimizationTheoryGeneratorBase.cs +++ b/Proxytrace.Domain/OptimizationTheory/Internal/OptimizationTheoryGeneratorBase.cs @@ -10,14 +10,23 @@ protected OptimizationTheoryGeneratorBase(IRepository repos this.repository = repository; } + /// + /// Generates asynchronously. + /// public abstract Task GenerateAsync(CancellationToken cancellationToken = default); + /// + /// Creates asynchronously. + /// public async Task CreateAsync(CancellationToken cancellationToken = default) { var instance = await GenerateAsync(cancellationToken); return (T)await repository.AddAsync(instance, cancellationToken); } + /// + /// Gets the or create asynchronously. + /// public async Task GetOrCreateAsync(CancellationToken cancellationToken = default) { var existing = await repository.FindFirstAsync(cancellationToken); diff --git a/Proxytrace.Domain/OptimizationTheory/Internal/SystemPromptTheory.cs b/Proxytrace.Domain/OptimizationTheory/Internal/SystemPromptTheory.cs index f2ef714fe..2d7098a18 100644 --- a/Proxytrace.Domain/OptimizationTheory/Internal/SystemPromptTheory.cs +++ b/Proxytrace.Domain/OptimizationTheory/Internal/SystemPromptTheory.cs @@ -12,9 +12,18 @@ namespace Proxytrace.Domain.OptimizationTheory.Internal; [UsedImplicitly] internal record SystemPromptTheory : OptimizationTheory, ISystemPromptTheory { + /// + /// Gets the kind. + /// public override ProposalKind Kind => ProposalKind.SystemPrompt; + /// + /// Gets or sets the proposed system message. + /// public string ProposedSystemMessage { get; private init; } + /// + /// Initializes a new instance of the class. + /// public SystemPromptTheory( IAgent agent, ITestSuite suite, @@ -31,6 +40,9 @@ public SystemPromptTheory( ProposedSystemMessage = proposedSystemMessage; } + /// + /// Initializes a new instance of the class. + /// public SystemPromptTheory( IAgent agent, ITestSuite suite, @@ -54,6 +66,9 @@ public SystemPromptTheory( ProposedSystemMessage = proposedSystemMessage; } + /// + /// Validates. + /// public override IEnumerable Validate(ValidationContext validationContext) { foreach (var result in base.Validate(validationContext)) diff --git a/Proxytrace.Domain/OptimizationTheory/Internal/SystemPromptTheoryGenerator.cs b/Proxytrace.Domain/OptimizationTheory/Internal/SystemPromptTheoryGenerator.cs index bed0bfc7a..46ce41ee1 100644 --- a/Proxytrace.Domain/OptimizationTheory/Internal/SystemPromptTheoryGenerator.cs +++ b/Proxytrace.Domain/OptimizationTheory/Internal/SystemPromptTheoryGenerator.cs @@ -12,6 +12,9 @@ internal class SystemPromptTheoryGenerator : OptimizationTheoryGeneratorBase suiteGenerator; private readonly IRandom random; + /// + /// Initializes a new instance of the class. + /// public SystemPromptTheoryGenerator( ISystemPromptTheory.CreateNew factory, IDomainEntityGenerator agentGenerator, @@ -25,6 +28,9 @@ public SystemPromptTheoryGenerator( this.random = random; } + /// + /// Generates asynchronously. + /// public override async Task GenerateAsync(CancellationToken cancellationToken = default) { var agent = await agentGenerator.GetOrCreateAsync(cancellationToken); diff --git a/Proxytrace.Domain/OptimizationTheory/Internal/ToolUpdateTheory.cs b/Proxytrace.Domain/OptimizationTheory/Internal/ToolUpdateTheory.cs index 8f7fd282f..889cfa102 100644 --- a/Proxytrace.Domain/OptimizationTheory/Internal/ToolUpdateTheory.cs +++ b/Proxytrace.Domain/OptimizationTheory/Internal/ToolUpdateTheory.cs @@ -13,9 +13,18 @@ namespace Proxytrace.Domain.OptimizationTheory.Internal; [UsedImplicitly] internal record ToolUpdateTheory : OptimizationTheory, IToolUpdateTheory { + /// + /// Gets the kind. + /// public override ProposalKind Kind => ProposalKind.Tool; + /// + /// Gets or sets the proposed tools. + /// public IReadOnlyList ProposedTools { get; private init; } + /// + /// Initializes a new instance of the class. + /// public ToolUpdateTheory( IAgent agent, ITestSuite suite, @@ -32,6 +41,9 @@ public ToolUpdateTheory( ProposedTools = proposedTools.ToArray(); } + /// + /// Initializes a new instance of the class. + /// public ToolUpdateTheory( IAgent agent, ITestSuite suite, @@ -55,6 +67,9 @@ public ToolUpdateTheory( ProposedTools = proposedTools.ToArray(); } + /// + /// Validates. + /// public override IEnumerable Validate(ValidationContext validationContext) { foreach (var result in base.Validate(validationContext)) diff --git a/Proxytrace.Domain/OptimizationTheory/Internal/ToolUpdateTheoryGenerator.cs b/Proxytrace.Domain/OptimizationTheory/Internal/ToolUpdateTheoryGenerator.cs index 65cdd36b4..d6df47ee3 100644 --- a/Proxytrace.Domain/OptimizationTheory/Internal/ToolUpdateTheoryGenerator.cs +++ b/Proxytrace.Domain/OptimizationTheory/Internal/ToolUpdateTheoryGenerator.cs @@ -12,6 +12,9 @@ internal class ToolUpdateTheoryGenerator : OptimizationTheoryGeneratorBase suiteGenerator; private readonly IRandom random; + /// + /// Initializes a new instance of the class. + /// public ToolUpdateTheoryGenerator( IToolUpdateTheory.CreateNew factory, IDomainEntityGenerator agentGenerator, @@ -25,6 +28,9 @@ public ToolUpdateTheoryGenerator( this.random = random; } + /// + /// Generates asynchronously. + /// public override async Task GenerateAsync(CancellationToken cancellationToken = default) { var agent = await agentGenerator.GetOrCreateAsync(cancellationToken); diff --git a/Proxytrace.Domain/PasswordResetToken/IPasswordResetToken.cs b/Proxytrace.Domain/PasswordResetToken/IPasswordResetToken.cs index b991d4d48..98cc85fcb 100644 --- a/Proxytrace.Domain/PasswordResetToken/IPasswordResetToken.cs +++ b/Proxytrace.Domain/PasswordResetToken/IPasswordResetToken.cs @@ -30,6 +30,12 @@ public interface IPasswordResetToken : IDomainEntity /// Marks the token as redeemed and persists. Task MarkConsumedAsync(CancellationToken cancellationToken = default); + /// + /// Factory delegate for creating a new new instance. + /// public delegate IPasswordResetToken CreateNew(IUser user, string tokenHash, DateTimeOffset expiresAt); + /// + /// Factory delegate for creating a new existing instance. + /// public delegate IPasswordResetToken CreateExisting(IUser user, string tokenHash, DateTimeOffset expiresAt, DateTimeOffset? consumedAt, IDomainEntityData existing); } diff --git a/Proxytrace.Domain/PasswordResetToken/IPasswordResetTokenRepository.cs b/Proxytrace.Domain/PasswordResetToken/IPasswordResetTokenRepository.cs index 5708f3d38..29e4c02bb 100644 --- a/Proxytrace.Domain/PasswordResetToken/IPasswordResetTokenRepository.cs +++ b/Proxytrace.Domain/PasswordResetToken/IPasswordResetTokenRepository.cs @@ -1,5 +1,8 @@ namespace Proxytrace.Domain.PasswordResetToken; +/// +/// Repository for persisting and querying password reset token entities. +/// public interface IPasswordResetTokenRepository : IRepository { Task FindByTokenAsync(string token, CancellationToken cancellationToken = default); diff --git a/Proxytrace.Domain/PasswordResetToken/Internal/PasswordResetToken.cs b/Proxytrace.Domain/PasswordResetToken/Internal/PasswordResetToken.cs index bfcec84c4..79a2ac394 100644 --- a/Proxytrace.Domain/PasswordResetToken/Internal/PasswordResetToken.cs +++ b/Proxytrace.Domain/PasswordResetToken/Internal/PasswordResetToken.cs @@ -7,11 +7,26 @@ namespace Proxytrace.Domain.PasswordResetToken.Internal; internal record PasswordResetToken : DomainEntity, IPasswordResetToken { + /// + /// Gets the user. + /// public IUser User { get; } + /// + /// Gets the token hash. + /// public string TokenHash { get; } + /// + /// Gets the expires at. + /// public DateTimeOffset ExpiresAt { get; } + /// + /// Gets or sets the consumed at. + /// public DateTimeOffset? ConsumedAt { get; private init; } + /// + /// Initializes a new instance of the class. + /// public PasswordResetToken( IUser user, string tokenHash, @@ -23,6 +38,9 @@ public PasswordResetToken( ExpiresAt = expiresAt; } + /// + /// Initializes a new instance of the class. + /// public PasswordResetToken( IUser user, string tokenHash, @@ -37,6 +55,9 @@ public PasswordResetToken( ConsumedAt = consumedAt; } + /// + /// Mark consumed asynchronously. + /// public Task MarkConsumedAsync(CancellationToken cancellationToken = default) { if (ConsumedAt is not null) @@ -46,6 +67,9 @@ public Task MarkConsumedAsync(CancellationToken cancellatio return ApplyAsync(this with { ConsumedAt = DateTimeOffset.UtcNow }, cancellationToken); } + /// + /// Validates. + /// public override IEnumerable Validate(ValidationContext validationContext) { foreach (var result in base.Validate(validationContext)) diff --git a/Proxytrace.Domain/PasswordResetToken/Internal/PasswordResetTokenGenerator.cs b/Proxytrace.Domain/PasswordResetToken/Internal/PasswordResetTokenGenerator.cs index f83e198b1..74c04e569 100644 --- a/Proxytrace.Domain/PasswordResetToken/Internal/PasswordResetTokenGenerator.cs +++ b/Proxytrace.Domain/PasswordResetToken/Internal/PasswordResetTokenGenerator.cs @@ -10,6 +10,9 @@ internal class PasswordResetTokenGenerator : DomainEntityGenerator users; + /// + /// Initializes a new instance of the class. + /// public PasswordResetTokenGenerator( IPasswordResetToken.CreateNew factory, IDomainEntityGenerator users, @@ -20,6 +23,9 @@ public PasswordResetTokenGenerator( this.users = users; } + /// + /// Generates asynchronously. + /// public override async Task GenerateAsync(CancellationToken cancellationToken = default) { var user = await users.GetOrCreateAsync(cancellationToken); diff --git a/Proxytrace.Domain/Project/Internal/Project.cs b/Proxytrace.Domain/Project/Internal/Project.cs index 933c21687..a7baef043 100644 --- a/Proxytrace.Domain/Project/Internal/Project.cs +++ b/Proxytrace.Domain/Project/Internal/Project.cs @@ -8,10 +8,22 @@ namespace Proxytrace.Domain.Project.Internal; internal record Project : DomainEntity, IProject { + /// + /// Gets the name. + /// public string Name { get; } + /// + /// Gets the system endpoint. + /// public IModelEndpoint SystemEndpoint { get; } + /// + /// Gets the members. + /// public IReadOnlyCollection Members { get; } + /// + /// Initializes a new instance of the class. + /// public Project( string name, IModelEndpoint systemEndpoint, @@ -23,6 +35,9 @@ public Project( Members = members.ToArray(); } + /// + /// Initializes a new instance of the class. + /// public Project( string name, IModelEndpoint systemEndpoint, @@ -35,6 +50,9 @@ public Project( Members = members.ToArray(); } + /// + /// Validates. + /// public override IEnumerable Validate(ValidationContext validationContext) { foreach (var result in base.Validate(validationContext)) diff --git a/Proxytrace.Domain/Project/Internal/ProjectGenerator.cs b/Proxytrace.Domain/Project/Internal/ProjectGenerator.cs index b0bec557f..029676aec 100644 --- a/Proxytrace.Domain/Project/Internal/ProjectGenerator.cs +++ b/Proxytrace.Domain/Project/Internal/ProjectGenerator.cs @@ -9,6 +9,9 @@ internal class ProjectGenerator : DomainEntityGenerator private readonly IProject.CreateNew factory; private readonly IDomainEntityGenerator endpointGenerator; + /// + /// Initializes a new instance of the class. + /// public ProjectGenerator( IProject.CreateNew factory, IRepository repository, @@ -19,6 +22,9 @@ public ProjectGenerator( this.endpointGenerator = endpointGenerator; } + /// + /// Generates asynchronously. + /// public override async Task GenerateAsync(CancellationToken cancellationToken = default) { var endpoint = await endpointGenerator.GetOrCreateAsync(cancellationToken); diff --git a/Proxytrace.Domain/ProjectSearchSettings/Internal/ProjectSearchSettings.cs b/Proxytrace.Domain/ProjectSearchSettings/Internal/ProjectSearchSettings.cs index 30b3fd2ab..77eac8a02 100644 --- a/Proxytrace.Domain/ProjectSearchSettings/Internal/ProjectSearchSettings.cs +++ b/Proxytrace.Domain/ProjectSearchSettings/Internal/ProjectSearchSettings.cs @@ -8,15 +8,39 @@ namespace Proxytrace.Domain.ProjectSearchSettings.Internal; internal record ProjectSearchSettings : DomainEntity, IProjectSearchSettings { + /// + /// The min snippet length constant value. + /// public const int MinSnippetLength = 20; + /// + /// The max snippet length constant value. + /// public const int MaxSnippetLength = 1000; + /// + /// Gets the project. + /// public IProject Project { get; } + /// + /// Gets the enabled. + /// public bool Enabled { get; } + /// + /// Gets the indexed kinds. + /// public IReadOnlyCollection IndexedKinds { get; } + /// + /// Gets the auto reindex on change. + /// public bool AutoReindexOnChange { get; } + /// + /// Gets the snippet length. + /// public int SnippetLength { get; } + /// + /// Initializes a new instance of the class. + /// public ProjectSearchSettings( IProject project, bool enabled, @@ -32,6 +56,9 @@ public ProjectSearchSettings( SnippetLength = snippetLength; } + /// + /// Initializes a new instance of the class. + /// public ProjectSearchSettings( IProject project, bool enabled, @@ -48,6 +75,9 @@ public ProjectSearchSettings( SnippetLength = snippetLength; } + /// + /// Validates. + /// public override IEnumerable Validate(ValidationContext validationContext) { foreach (var result in base.Validate(validationContext)) diff --git a/Proxytrace.Domain/ProjectSearchSettings/Internal/ProjectSearchSettingsGenerator.cs b/Proxytrace.Domain/ProjectSearchSettings/Internal/ProjectSearchSettingsGenerator.cs index 532a24239..8b080feaa 100644 --- a/Proxytrace.Domain/ProjectSearchSettings/Internal/ProjectSearchSettingsGenerator.cs +++ b/Proxytrace.Domain/ProjectSearchSettings/Internal/ProjectSearchSettingsGenerator.cs @@ -10,6 +10,9 @@ internal class ProjectSearchSettingsGenerator : DomainEntityGenerator projectGenerator; + /// + /// Initializes a new instance of the class. + /// public ProjectSearchSettingsGenerator( IProjectSearchSettings.CreateNew factory, IRepository repository, @@ -20,6 +23,9 @@ public ProjectSearchSettingsGenerator( this.projectGenerator = projectGenerator; } + /// + /// Generates asynchronously. + /// public override async Task GenerateAsync(CancellationToken cancellationToken = default) { var project = await projectGenerator.CreateAsync(cancellationToken); diff --git a/Proxytrace.Domain/Prompt/IPromptTemplateRepository.cs b/Proxytrace.Domain/Prompt/IPromptTemplateRepository.cs index c65380042..c5c1a801d 100644 --- a/Proxytrace.Domain/Prompt/IPromptTemplateRepository.cs +++ b/Proxytrace.Domain/Prompt/IPromptTemplateRepository.cs @@ -18,8 +18,14 @@ public interface IPromptTemplateRepository Task FindAsync(string name, CancellationToken cancellationToken = default); } +/// +/// The exception that is thrown when a prompt not found error occurs. +/// public class PromptNotFoundException : Exception { + /// + /// Initializes a new instance of the class. + /// public PromptNotFoundException(string name) : base($"Prompt with name '{name}' not found.") { } } diff --git a/Proxytrace.Domain/Prompt/Internal/ResourcesPromptRepository.cs b/Proxytrace.Domain/Prompt/Internal/ResourcesPromptRepository.cs index 6ff5db22c..548d981f6 100644 --- a/Proxytrace.Domain/Prompt/Internal/ResourcesPromptRepository.cs +++ b/Proxytrace.Domain/Prompt/Internal/ResourcesPromptRepository.cs @@ -12,6 +12,9 @@ internal class ResourcesPromptRepository : IPromptTemplateRepository private readonly IReadOnlyCollection resources; private readonly IPromptTemplate.Create createTemplate; + /// + /// Initializes a new instance of the class. + /// public ResourcesPromptRepository( IReadOnlyCollection resources, IPromptTemplate.Create createTemplate) diff --git a/Proxytrace.Domain/Proposal/IProposal.cs b/Proxytrace.Domain/Proposal/IProposal.cs index 9bd30baa3..a6d62dbbf 100644 --- a/Proxytrace.Domain/Proposal/IProposal.cs +++ b/Proxytrace.Domain/Proposal/IProposal.cs @@ -1,5 +1,8 @@ namespace Proxytrace.Domain.Proposal; +/// +/// Represents a proposal. +/// public interface IProposal : IDomainEntity { diff --git a/Proxytrace.Domain/Proposal/OptimizationContentHash.cs b/Proxytrace.Domain/Proposal/OptimizationContentHash.cs index e3e051690..ca9fa11f4 100644 --- a/Proxytrace.Domain/Proposal/OptimizationContentHash.cs +++ b/Proxytrace.Domain/Proposal/OptimizationContentHash.cs @@ -15,6 +15,9 @@ namespace Proxytrace.Domain.Proposal; /// internal static class OptimizationContentHash { + /// + /// For system prompt. + /// public static string ForSystemPrompt(ISerializer serializer, Guid agentId, string proposedSystemMessage) => Hash(serializer, new { @@ -23,6 +26,9 @@ public static string ForSystemPrompt(ISerializer serializer, Guid agentId, strin Payload = new { Message = NormalizeText(proposedSystemMessage) }, }); + /// + /// For model switch. + /// public static string ForModelSwitch(ISerializer serializer, Guid agentId, Guid proposedEndpointId) => Hash(serializer, new { @@ -31,6 +37,9 @@ public static string ForModelSwitch(ISerializer serializer, Guid agentId, Guid p Payload = new { EndpointId = proposedEndpointId }, }); + /// + /// For tools. + /// public static string ForTools(ISerializer serializer, Guid agentId, IReadOnlyList proposedTools) { var orderedTools = proposedTools diff --git a/Proxytrace.Domain/Proposal/Priority.cs b/Proxytrace.Domain/Proposal/Priority.cs index 4af7d730f..087293d1b 100644 --- a/Proxytrace.Domain/Proposal/Priority.cs +++ b/Proxytrace.Domain/Proposal/Priority.cs @@ -1,5 +1,8 @@ namespace Proxytrace.Domain.Proposal; +/// +/// Specifies the priority. +/// public enum Priority { Low = 0, diff --git a/Proxytrace.Domain/Search/ISearchIndexer.cs b/Proxytrace.Domain/Search/ISearchIndexer.cs index e6ca0470c..2ff3c1372 100644 --- a/Proxytrace.Domain/Search/ISearchIndexer.cs +++ b/Proxytrace.Domain/Search/ISearchIndexer.cs @@ -1,5 +1,8 @@ namespace Proxytrace.Domain.Search; +/// +/// Represents a search indexer. +/// public interface ISearchIndexer { Task IndexAsync(SearchKind kind, Guid projectId, Guid entityId, CancellationToken cancellationToken = default); diff --git a/Proxytrace.Domain/Search/ISearchService.cs b/Proxytrace.Domain/Search/ISearchService.cs index c65af0228..0fb4b1bde 100644 --- a/Proxytrace.Domain/Search/ISearchService.cs +++ b/Proxytrace.Domain/Search/ISearchService.cs @@ -1,5 +1,8 @@ namespace Proxytrace.Domain.Search; +/// +/// Service that provides search functionality. +/// public interface ISearchService { Task SearchAsync(Guid projectId, string query, CancellationToken cancellationToken = default); diff --git a/Proxytrace.Domain/Search/ISearchable.cs b/Proxytrace.Domain/Search/ISearchable.cs index 29fb7d889..746650e50 100644 --- a/Proxytrace.Domain/Search/ISearchable.cs +++ b/Proxytrace.Domain/Search/ISearchable.cs @@ -1,5 +1,8 @@ namespace Proxytrace.Domain.Search; +/// +/// Represents a searchable. +/// public interface ISearchable : IProjectSpecific { SearchKind SearchKind { get; } diff --git a/Proxytrace.Domain/Search/SearchHit.cs b/Proxytrace.Domain/Search/SearchHit.cs index 46ce654bb..63c036f4d 100644 --- a/Proxytrace.Domain/Search/SearchHit.cs +++ b/Proxytrace.Domain/Search/SearchHit.cs @@ -1,5 +1,8 @@ namespace Proxytrace.Domain.Search; +/// +/// Represents a search hit. +/// public sealed record SearchHit( SearchKind Kind, Guid EntityId, diff --git a/Proxytrace.Domain/Search/SearchKind.cs b/Proxytrace.Domain/Search/SearchKind.cs index c25b991ef..cc3be8453 100644 --- a/Proxytrace.Domain/Search/SearchKind.cs +++ b/Proxytrace.Domain/Search/SearchKind.cs @@ -1,5 +1,8 @@ namespace Proxytrace.Domain.Search; +/// +/// Specifies the search kind. +/// public enum SearchKind { Agent = 0, diff --git a/Proxytrace.Domain/Search/SearchResults.cs b/Proxytrace.Domain/Search/SearchResults.cs index 3335ad3ca..7ee42e7cd 100644 --- a/Proxytrace.Domain/Search/SearchResults.cs +++ b/Proxytrace.Domain/Search/SearchResults.cs @@ -1,3 +1,6 @@ namespace Proxytrace.Domain.Search; +/// +/// Represents a search results. +/// public sealed record SearchResults(IReadOnlyList Hits); diff --git a/Proxytrace.Domain/Session/ISession.cs b/Proxytrace.Domain/Session/ISession.cs index 56f41877c..24d64ee19 100644 --- a/Proxytrace.Domain/Session/ISession.cs +++ b/Proxytrace.Domain/Session/ISession.cs @@ -21,6 +21,9 @@ public interface ISession : IDomainEntity long TotalTokens { get; } + /// + /// Factory delegate for creating a new new instance. + /// public delegate ISession CreateNew( string externalKey, Guid projectId, @@ -28,6 +31,9 @@ public delegate ISession CreateNew( int traceCount, long totalTokens); + /// + /// Factory delegate for creating a new existing instance. + /// public delegate ISession CreateExisting( string externalKey, Guid projectId, diff --git a/Proxytrace.Domain/Session/ISessionRepository.cs b/Proxytrace.Domain/Session/ISessionRepository.cs index a343b2a47..2c1f9bb06 100644 --- a/Proxytrace.Domain/Session/ISessionRepository.cs +++ b/Proxytrace.Domain/Session/ISessionRepository.cs @@ -6,6 +6,9 @@ namespace Proxytrace.Domain.Session; /// public readonly record struct SessionTraceRemoval(Guid SessionId, int TraceCount, long TotalTokens); +/// +/// Repository for persisting and querying session entities. +/// public interface ISessionRepository : IRepository { /// diff --git a/Proxytrace.Domain/Session/Internal/Session.cs b/Proxytrace.Domain/Session/Internal/Session.cs index 00019a3c1..c2093c128 100644 --- a/Proxytrace.Domain/Session/Internal/Session.cs +++ b/Proxytrace.Domain/Session/Internal/Session.cs @@ -6,12 +6,30 @@ namespace Proxytrace.Domain.Session.Internal; internal record Session : DomainEntity, ISession { + /// + /// Gets the external key. + /// public string ExternalKey { get; } + /// + /// Gets the project id. + /// public Guid ProjectId { get; } + /// + /// Gets the last activity at. + /// public DateTimeOffset LastActivityAt { get; } + /// + /// Gets the trace count. + /// public int TraceCount { get; } + /// + /// Gets the total tokens. + /// public long TotalTokens { get; } + /// + /// Initializes a new instance of the class. + /// public Session( string externalKey, Guid projectId, @@ -27,6 +45,9 @@ public Session( TotalTokens = totalTokens; } + /// + /// Initializes a new instance of the class. + /// public Session( string externalKey, Guid projectId, @@ -43,6 +64,9 @@ public Session( TotalTokens = totalTokens; } + /// + /// Validates. + /// public override IEnumerable Validate(ValidationContext validationContext) { foreach (var result in base.Validate(validationContext)) diff --git a/Proxytrace.Domain/Session/Internal/SessionGenerator.cs b/Proxytrace.Domain/Session/Internal/SessionGenerator.cs index 1d7d88189..26e47423b 100644 --- a/Proxytrace.Domain/Session/Internal/SessionGenerator.cs +++ b/Proxytrace.Domain/Session/Internal/SessionGenerator.cs @@ -9,6 +9,9 @@ internal class SessionGenerator : DomainEntityGenerator private readonly ISession.CreateNew factory; private readonly IDomainEntityGenerator projectGenerator; + /// + /// Initializes a new instance of the class. + /// public SessionGenerator( ISession.CreateNew factory, IRepository repository, @@ -19,6 +22,9 @@ public SessionGenerator( this.projectGenerator = projectGenerator; } + /// + /// Generates asynchronously. + /// public override async Task GenerateAsync(CancellationToken cancellationToken = default) { var project = await projectGenerator.GetOrCreateAsync(cancellationToken); diff --git a/Proxytrace.Domain/Session/SessionIdDerivation.cs b/Proxytrace.Domain/Session/SessionIdDerivation.cs index 1856e0369..7e953aa13 100644 --- a/Proxytrace.Domain/Session/SessionIdDerivation.cs +++ b/Proxytrace.Domain/Session/SessionIdDerivation.cs @@ -11,6 +11,9 @@ namespace Proxytrace.Domain.Session; /// public static class SessionIdDerivation { + /// + /// Derive. + /// public static Guid Derive(Guid projectId, string externalKey) { var keyBytes = Encoding.UTF8.GetBytes(externalKey); @@ -21,6 +24,9 @@ public static Guid Derive(Guid projectId, string externalKey) return new Guid(hash.AsSpan(0, 16)); } + /// + /// Truncates the key. + /// public static string TruncateKey(string raw) => raw.Length <= ISession.MaxExternalKeyLength ? raw : raw[..ISession.MaxExternalKeyLength]; } diff --git a/Proxytrace.Domain/Statistics/IStatsWriter.cs b/Proxytrace.Domain/Statistics/IStatsWriter.cs index 7aecf7d19..8e66d9672 100644 --- a/Proxytrace.Domain/Statistics/IStatsWriter.cs +++ b/Proxytrace.Domain/Statistics/IStatsWriter.cs @@ -1,5 +1,8 @@ namespace Proxytrace.Domain.Statistics; +/// +/// Writes stats data. +/// public interface IStatsWriter { Task UpsertAsync(TStats stats, CancellationToken cancellationToken = default); diff --git a/Proxytrace.Domain/Statistics/StatisticsBucket.cs b/Proxytrace.Domain/Statistics/StatisticsBucket.cs index 41c6bd5ea..e014f71e0 100644 --- a/Proxytrace.Domain/Statistics/StatisticsBucket.cs +++ b/Proxytrace.Domain/Statistics/StatisticsBucket.cs @@ -1,5 +1,8 @@ namespace Proxytrace.Domain.Statistics; +/// +/// Specifies the statistics bucket. +/// public enum StatisticsBucket { FiveMinutes, @@ -7,6 +10,9 @@ public enum StatisticsBucket Daily, } +/// +/// Represents a statistics time. +/// public static class StatisticsTime { /// diff --git a/Proxytrace.Domain/Statistics/StatisticsRecords.cs b/Proxytrace.Domain/Statistics/StatisticsRecords.cs index 90cb07f16..d459248d2 100644 --- a/Proxytrace.Domain/Statistics/StatisticsRecords.cs +++ b/Proxytrace.Domain/Statistics/StatisticsRecords.cs @@ -4,6 +4,9 @@ namespace Proxytrace.Domain.Statistics; +/// +/// Represents a statistics filter. +/// public record StatisticsFilter( DateTimeOffset? From = null, DateTimeOffset? To = null, @@ -26,6 +29,9 @@ public record StatisticsFilter( // another caller's entry — a miss, never a false hit. IReadOnlyCollection? ProjectIds = null); +/// +/// Represents a statistics summary. +/// public record StatisticsSummary( long TotalCalls, long TotalInputTokens, @@ -34,6 +40,9 @@ public record StatisticsSummary( double AvgLatencyMs, double? OverallPassRate); +/// +/// Represents a token usage stat. +/// public record TokenUsageStat( DateTimeOffset BucketStart, Guid EndpointId, @@ -41,6 +50,9 @@ public record TokenUsageStat( long? OutputTokens, long? CachedInputTokens); +/// +/// Represents a latency stat. +/// public record LatencyStat( Guid EndpointId, double P50Ms, @@ -50,18 +62,27 @@ public record LatencyStat( double MaxMs, int SampleCount); +/// +/// Represents a pass rate stat. +/// public record PassRateStat( Guid SuiteId, DateTimeOffset RunTimestamp, int PassCount, int FailCount); +/// +/// Represents a error rate stat. +/// public record ErrorRateStat( Guid EndpointId, int TotalCalls, int ErrorCalls, double ErrorRate); +/// +/// Represents a model breakdown stat. +/// public record ModelBreakdownStat( Guid EndpointId, string ModelName, @@ -71,6 +92,9 @@ public record ModelBreakdownStat( long? TotalCachedInputTokens, double? AvgDurationMs); +/// +/// Represents a agent breakdown stat. +/// public record AgentBreakdownStat( Guid AgentId, int CallCount); @@ -143,6 +167,9 @@ public record CallTrends( IReadOnlyList LatencyMs, IReadOnlyList Throughput); +/// +/// Represents a cost estimate stat. +/// public record CostEstimateStat( Guid EndpointId, decimal? InputCostEur, @@ -190,6 +217,9 @@ public record ApiKeyCostPoint( Guid? ApiKeyId, decimal CostEur); +/// +/// Represents a agent time series point. +/// public record AgentTimeSeriesPoint( DateTimeOffset BucketStart, int TraceCount, @@ -199,11 +229,17 @@ public record AgentTimeSeriesPoint( decimal CostEur, double AvgLatencyMs); +/// +/// Represents a agent pass rate point. +/// public record AgentPassRatePoint( DateTimeOffset BucketStart, int Passed, int TestCases); +/// +/// Represents a agent suite pass rate. +/// public record AgentSuitePassRate( Guid SuiteId, string SuiteName, @@ -211,12 +247,18 @@ public record AgentSuitePassRate( int Passed, int TestCases); +/// +/// Represents a agent entity counts. +/// public record AgentEntityCounts( int SuiteCount, int TestCaseCount, int OpenProposalCount, int TotalProposalCount); +/// +/// Represents a agent time summary. +/// public record AgentTimeSummary( int TotalTraces, long TotalInputTokens, @@ -225,6 +267,9 @@ public record AgentTimeSummary( decimal TotalCostEur, double AvgLatencyMs); +/// +/// Represents a agent overview stat. +/// public record AgentOverviewStat( AgentTimeSummary Summary, IReadOnlyList TimeSeries, @@ -248,6 +293,9 @@ public record MetricDistribution( double Max, IReadOnlyList Histogram) { + /// + /// Gets the empty. + /// public static MetricDistribution Empty => new(0d, 0d, 0, 0d, 0d, []); } @@ -264,6 +312,9 @@ public record AgentCallDistributions( MetricDistribution CacheHitRatePerConversation, MetricDistribution ToolCallsPerConversation) { + /// + /// Gets the empty. + /// public static AgentCallDistributions Empty => new( MetricDistribution.Empty, MetricDistribution.Empty, @@ -273,6 +324,9 @@ public record AgentCallDistributions( MetricDistribution.Empty); } +/// +/// Represents a evaluator summary. +/// public record EvaluatorSummary( int TotalEvaluations, double? AvgScore, @@ -283,6 +337,9 @@ public record EvaluatorSummary( decimal? TotalCost, double? AvgLatencyMs); +/// +/// Represents a evaluator cost point. +/// public record EvaluatorCostPoint( DateTimeOffset BucketStart, long InputTokens, @@ -291,21 +348,33 @@ public record EvaluatorCostPoint( decimal Cost, double AvgLatencyMs); +/// +/// Represents a evaluator pass rate point. +/// public record EvaluatorPassRatePoint( DateTimeOffset BucketStart, int Passed, int Total); +/// +/// Represents a evaluator score bucket. +/// public record EvaluatorScoreBucket( string Score, int Count); +/// +/// Represents a evaluator overview stat. +/// public record EvaluatorOverviewStat( EvaluatorSummary Summary, IReadOnlyList PassRateTrend, IReadOnlyList ScoreDistribution, IReadOnlyList CostTrend); +/// +/// Represents a evaluator sparkline stat. +/// public record EvaluatorSparklineStat( Guid EvaluatorId, IReadOnlyList Points); @@ -322,11 +391,20 @@ public record TestRunStatsAggregate( TokenUsage? Usage, decimal? Cost) { + /// + /// Gets the failed. + /// public int Failed => TestCases - Passed; + /// + /// Provides additional functionality. + /// public double? PassRate => TestCases > 0 ? Passed / (double)TestCases : null; + /// + /// Gets the empty. + /// public static TestRunStatsAggregate Empty => new( TestCases: 0, Passed: 0, @@ -334,6 +412,9 @@ public double? PassRate Usage: null, Cost: null); + /// + /// Defines the -(TestRunStatsAggregate operator for the current type. + /// public static TestRunStatsAggregate operator -(TestRunStatsAggregate a, TestRunStatsAggregate b) => new( TestCases: a.TestCases - b.TestCases, diff --git a/Proxytrace.Domain/Statistics/TestRun/TestRunStats.cs b/Proxytrace.Domain/Statistics/TestRun/TestRunStats.cs index efcf6cd52..a33c2a0f6 100644 --- a/Proxytrace.Domain/Statistics/TestRun/TestRunStats.cs +++ b/Proxytrace.Domain/Statistics/TestRun/TestRunStats.cs @@ -18,11 +18,20 @@ public record TestRunStats( decimal? Cost, DateTimeOffset RunCompletedAt) { + /// + /// Gets the failed. + /// public int Failed => TestCases - Passed; + /// + /// Provides additional functionality. + /// public double? PassRate => TestCases > 0 ? Passed / (double)TestCases : null; + /// + /// Represents a filter. + /// public record Filter( Guid? AgentId = null, IReadOnlyCollection? AgentIds = null, diff --git a/Proxytrace.Domain/TestCase/Internal/TestCase.cs b/Proxytrace.Domain/TestCase/Internal/TestCase.cs index 5a283d189..252c90277 100644 --- a/Proxytrace.Domain/TestCase/Internal/TestCase.cs +++ b/Proxytrace.Domain/TestCase/Internal/TestCase.cs @@ -7,11 +7,23 @@ namespace Proxytrace.Domain.TestCase.Internal; internal record TestCase : DomainEntity, ITestCase { + /// + /// Gets the input. + /// public Conversation Input { get; } + /// + /// Gets the expected output. + /// public AssistantMessage ExpectedOutput { get; } + /// + /// Gets the source agent call id. + /// public Guid? SourceAgentCallId { get; } // CreateNewFromCall: promote a trace as-is — expected output is the response the agent recorded. + /// + /// Initializes a new instance of the class. + /// public TestCase( IAgentCall agentCall, IRepository repository) : this( @@ -26,6 +38,9 @@ public TestCase( // CreateCorrection: record a human correction — the agent saw this input, and the right answer was // expectedOutput. Keeps the link back to the source trace so a rejected output becomes a regression // test with traceable provenance. + /// + /// Initializes a new instance of the class. + /// public TestCase( IAgentCall agentCall, AssistantMessage expectedOutput, @@ -42,6 +57,9 @@ public TestCase( // than overloading on (Conversation, AssistantMessage): IAgentCall is a container-resolvable entity, // so a bare (Conversation, AssistantMessage) ctor would tie with the CreateCorrection ctor and make // the Autofac delegate factory ambiguous — the distinct arity keeps each delegate unambiguous.) + /// + /// Initializes a new instance of the class. + /// public TestCase( Conversation input, AssistantMessage expectedOutput, @@ -53,6 +71,9 @@ public TestCase( SourceAgentCallId = sourceAgentCallId; } + /// + /// Initializes a new instance of the class. + /// public TestCase( Conversation input, AssistantMessage expectedOutput, @@ -65,6 +86,9 @@ public TestCase( SourceAgentCallId = sourceAgentCallId; } + /// + /// Gets the summary. + /// public string GetSummary(int maxLength = 77) { var firstUser = Input.Messages.OfType().FirstOrDefault(); @@ -73,6 +97,9 @@ public string GetSummary(int maxLength = 77) return text.Length > maxLength + 3 ? text[..maxLength] + "…" : text; } + /// + /// Validates. + /// public override IEnumerable Validate(ValidationContext validationContext) { foreach (var result in base.Validate(validationContext)) diff --git a/Proxytrace.Domain/TestCase/Internal/TestCaseGenerator.cs b/Proxytrace.Domain/TestCase/Internal/TestCaseGenerator.cs index af8b7072b..00e62c095 100644 --- a/Proxytrace.Domain/TestCase/Internal/TestCaseGenerator.cs +++ b/Proxytrace.Domain/TestCase/Internal/TestCaseGenerator.cs @@ -10,6 +10,9 @@ internal class TestCaseGenerator : DomainEntityGenerator private readonly IDomainObjectGenerator conversationGenerator; private readonly IDomainObjectGenerator assistantMessageGenerator; + /// + /// Initializes a new instance of the class. + /// public TestCaseGenerator( ITestCase.CreateNew factory, IRepository repository, @@ -22,6 +25,9 @@ public TestCaseGenerator( this.assistantMessageGenerator = assistantMessageGenerator; } + /// + /// Generates asynchronously. + /// public override async Task GenerateAsync(CancellationToken cancellationToken = default) => factory( input: await conversationGenerator.CreateAsync(cancellationToken), diff --git a/Proxytrace.Domain/TestResult/ITestResultGenerator.cs b/Proxytrace.Domain/TestResult/ITestResultGenerator.cs index 163cf7e4d..8a3719902 100644 --- a/Proxytrace.Domain/TestResult/ITestResultGenerator.cs +++ b/Proxytrace.Domain/TestResult/ITestResultGenerator.cs @@ -2,6 +2,9 @@ namespace Proxytrace.Domain.TestResult; +/// +/// Generates test result instances. +/// public interface ITestResultGenerator : IDomainEntityGenerator { Task CreateAsync(ITestCase testCase, CancellationToken cancellationToken = default); diff --git a/Proxytrace.Domain/TestResult/Internal/TestResult.cs b/Proxytrace.Domain/TestResult/Internal/TestResult.cs index 3226a0858..1ec8c77a9 100644 --- a/Proxytrace.Domain/TestResult/Internal/TestResult.cs +++ b/Proxytrace.Domain/TestResult/Internal/TestResult.cs @@ -9,14 +9,38 @@ namespace Proxytrace.Domain.TestResult.Internal; internal record TestResult : DomainEntity, ITestResult { + /// + /// Gets or sets the test case. + /// public ITestCase TestCase { get; init; } + /// + /// Gets or sets the actual response. + /// public AssistantMessage ActualResponse { get; init; } + /// + /// Gets the passed. + /// public bool Passed => this.IsPass(); + /// + /// Gets or sets the evaluations. + /// public IReadOnlyCollection Evaluations { get; init; } + /// + /// Gets or sets the latency. + /// public TimeSpan Latency { get; init; } + /// + /// Gets or sets the usage. + /// public TokenUsage? Usage { get; init; } + /// + /// Gets the overall score. + /// public EvaluationScore? OverallScore => Evaluations.CombineScores(); + /// + /// Initializes a new instance of the class. + /// public TestResult( ITestCase testCase, ICompletion completion, @@ -30,6 +54,9 @@ public TestResult( Usage = completion.Usage; } + /// + /// Initializes a new instance of the class. + /// public TestResult( ITestCase testCase, AssistantMessage actualResponse, @@ -46,6 +73,9 @@ public TestResult( Usage = usage; } + /// + /// Adds the evaluation asynchronously. + /// public Task AddEvaluationAsync(IEvaluation evaluation, CancellationToken cancellationToken = default) { IReadOnlyList updatedEvaluations = @@ -57,6 +87,9 @@ public Task AddEvaluationAsync(IEvaluation evaluation, Cancellation return ApplyAsync(this with { Evaluations = updatedEvaluations }, cancellationToken); } + /// + /// Validates. + /// public override IEnumerable Validate(ValidationContext validationContext) { foreach (var result in base.Validate(validationContext)) diff --git a/Proxytrace.Domain/TestResult/Internal/TestResultGenerator.cs b/Proxytrace.Domain/TestResult/Internal/TestResultGenerator.cs index d59f9bf79..35cb92b4a 100644 --- a/Proxytrace.Domain/TestResult/Internal/TestResultGenerator.cs +++ b/Proxytrace.Domain/TestResult/Internal/TestResultGenerator.cs @@ -14,6 +14,9 @@ internal class TestResultGenerator : DomainEntityGenerator, ITestRe private readonly IDomainObjectGenerator evaluationGenerator; private readonly IDomainObjectGenerator completionGenerator; + /// + /// Initializes a new instance of the class. + /// public TestResultGenerator( ITestResult.CreateNew factory, IRepository repository, @@ -28,6 +31,9 @@ public TestResultGenerator( this.completionGenerator = completionGenerator; } + /// + /// Generates asynchronously. + /// public override async Task GenerateAsync(CancellationToken cancellationToken = default) { IReadOnlyCollection evaluations = await Enumerable.Range(0, random.Int(1, 3)) @@ -40,6 +46,9 @@ public override async Task GenerateAsync(CancellationToken cancella evaluations: evaluations); } + /// + /// Creates asynchronously. + /// public async Task CreateAsync(ITestCase testCase, CancellationToken cancellationToken = default) { IReadOnlyCollection evaluations = await Enumerable.Range(0, random.Int(1, 3)) diff --git a/Proxytrace.Domain/TestResult/TestResultExtensions.cs b/Proxytrace.Domain/TestResult/TestResultExtensions.cs index 240e08f9b..05dd99713 100644 --- a/Proxytrace.Domain/TestResult/TestResultExtensions.cs +++ b/Proxytrace.Domain/TestResult/TestResultExtensions.cs @@ -18,6 +18,9 @@ namespace Proxytrace.Domain.TestResult; /// public static class TestResultExtensions { + /// + /// Determines whether the pass. + /// public static bool IsPass(this ITestResult result) { var judged = result.Evaluations.Where(e => !e.IsErrored()).ToList(); diff --git a/Proxytrace.Domain/TestRun/Internal/TestRun.cs b/Proxytrace.Domain/TestRun/Internal/TestRun.cs index 92ca5920f..a8a6cd384 100644 --- a/Proxytrace.Domain/TestRun/Internal/TestRun.cs +++ b/Proxytrace.Domain/TestRun/Internal/TestRun.cs @@ -9,13 +9,34 @@ namespace Proxytrace.Domain.TestRun.Internal; internal record TestRun : DomainEntity, ITestRun { + /// + /// Gets or sets the group. + /// public ITestRunGroup Group { get; init; } + /// + /// Gets or sets the endpoint. + /// public IModelEndpoint Endpoint { get; init; } + /// + /// Gets or sets the sample index. + /// public int SampleIndex { get; init; } + /// + /// Gets or sets the status. + /// public TestRunStatus Status { get; init; } + /// + /// Gets or sets the completed at. + /// public DateTimeOffset? CompletedAt { get; init; } + /// + /// Gets or sets the test results. + /// public IReadOnlyList TestResults { get; init; } + /// + /// Initializes a new instance of the class. + /// public TestRun( ITestRunGroup group, IModelEndpoint endpoint, @@ -30,6 +51,9 @@ public TestRun( TestResults = []; } + /// + /// Initializes a new instance of the class. + /// public TestRun( ITestRunGroup group, IModelEndpoint endpoint, @@ -48,6 +72,9 @@ public TestRun( TestResults = testResults.ToArray(); } + /// + /// Validates. + /// public override IEnumerable Validate(ValidationContext validationContext) { foreach (var result in base.Validate(validationContext)) @@ -68,6 +95,9 @@ public override IEnumerable Validate(ValidationContext validat } } + /// + /// Sets the test result. + /// public Task SetTestResult(ITestResult testResult, CancellationToken cancellationToken = default) { // A case can still finish in-flight after the run reached a terminal state (e.g. during @@ -96,15 +126,27 @@ public Task SetTestResult(ITestResult testResult, CancellationToken ca }, cancellationToken); } + /// + /// Sets the running. + /// public Task SetRunning(CancellationToken cancellationToken = default) => SetState(TestRunStatus.Running, cancellationToken); + /// + /// Sets the completed. + /// public Task SetCompleted(CancellationToken cancellationToken = default) => SetState(TestRunStatus.Completed, cancellationToken); + /// + /// Sets the failed. + /// public Task SetFailed(CancellationToken cancellationToken = default) => SetState(TestRunStatus.Failed, cancellationToken); + /// + /// Sets the cancelled. + /// public Task SetCancelled(CancellationToken cancellationToken = default) => SetState(TestRunStatus.Cancelled, cancellationToken); diff --git a/Proxytrace.Domain/TestRun/Internal/TestRunGenerator.cs b/Proxytrace.Domain/TestRun/Internal/TestRunGenerator.cs index ccd733967..54d488b91 100644 --- a/Proxytrace.Domain/TestRun/Internal/TestRunGenerator.cs +++ b/Proxytrace.Domain/TestRun/Internal/TestRunGenerator.cs @@ -14,6 +14,9 @@ internal class TestRunGenerator : DomainEntityGenerator private readonly IDomainEntityGenerator groupGenerator; private readonly ITestResultGenerator testResultGenerator; + /// + /// Initializes a new instance of the class. + /// public TestRunGenerator( ITestRun.CreateNew factory, IRepository repository, @@ -28,6 +31,9 @@ public TestRunGenerator( this.testResultGenerator = testResultGenerator; } + /// + /// Generates asynchronously. + /// public override async Task GenerateAsync(CancellationToken cancellationToken = default) { ITestRun run = factory( diff --git a/Proxytrace.Domain/TestRun/TestRunStatus.cs b/Proxytrace.Domain/TestRun/TestRunStatus.cs index 688d51710..d1d5c333c 100644 --- a/Proxytrace.Domain/TestRun/TestRunStatus.cs +++ b/Proxytrace.Domain/TestRun/TestRunStatus.cs @@ -1,5 +1,8 @@ namespace Proxytrace.Domain.TestRun; +/// +/// Specifies the test run status. +/// public enum TestRunStatus { Pending = 0, diff --git a/Proxytrace.Domain/TestRun/TestRunStatusExtensions.cs b/Proxytrace.Domain/TestRun/TestRunStatusExtensions.cs index 89da6d531..1354d7751 100644 --- a/Proxytrace.Domain/TestRun/TestRunStatusExtensions.cs +++ b/Proxytrace.Domain/TestRun/TestRunStatusExtensions.cs @@ -7,6 +7,9 @@ namespace Proxytrace.Domain.TestRun; /// public static class TestRunStatusExtensions { + /// + /// Determines whether the terminal. + /// public static bool IsTerminal(this TestRunStatus status) => status is TestRunStatus.Completed or TestRunStatus.Failed or TestRunStatus.Cancelled; } diff --git a/Proxytrace.Domain/TestRun/TestRunTotals.cs b/Proxytrace.Domain/TestRun/TestRunTotals.cs index 95f60d554..f999889c2 100644 --- a/Proxytrace.Domain/TestRun/TestRunTotals.cs +++ b/Proxytrace.Domain/TestRun/TestRunTotals.cs @@ -8,6 +8,9 @@ namespace Proxytrace.Domain.TestRun; /// public record TestRunTotals(decimal? CostEur, long? TokensIn, long? TokensOut, long? CachedTokensIn) { + /// + /// From. + /// public static TestRunTotals From(ITestRun run) { var usages = run.TestResults diff --git a/Proxytrace.Domain/TestRunGroup/ITestRunGroupRepository.cs b/Proxytrace.Domain/TestRunGroup/ITestRunGroupRepository.cs index 3def54e8b..f5a61a38b 100644 --- a/Proxytrace.Domain/TestRunGroup/ITestRunGroupRepository.cs +++ b/Proxytrace.Domain/TestRunGroup/ITestRunGroupRepository.cs @@ -3,6 +3,9 @@ namespace Proxytrace.Domain.TestRunGroup; +/// +/// Repository for persisting and querying test run group entities. +/// public interface ITestRunGroupRepository : IRepository { Task> GetByAgentAsync(Guid agentId, CancellationToken cancellationToken = default); diff --git a/Proxytrace.Domain/TestRunGroup/Internal/TestRunGroup.cs b/Proxytrace.Domain/TestRunGroup/Internal/TestRunGroup.cs index 943271cc9..d438aa22f 100644 --- a/Proxytrace.Domain/TestRunGroup/Internal/TestRunGroup.cs +++ b/Proxytrace.Domain/TestRunGroup/Internal/TestRunGroup.cs @@ -9,14 +9,38 @@ internal record TestRunGroup : DomainEntity, ITestRunGroup { private readonly ITestRunRepository testRuns; + /// + /// Gets the suite. + /// public ITestSuite Suite { get; } + /// + /// Gets or sets the status. + /// public TestRunStatus Status { get; private init; } + /// + /// Gets or sets the completed at. + /// public DateTimeOffset? CompletedAt { get; private init; } + /// + /// Gets the is system run. + /// public bool IsSystemRun { get; } + /// + /// Gets the schedule id. + /// public Guid? ScheduleId { get; } + /// + /// Gets the sample count. + /// public int SampleCount { get; } + /// + /// Gets or sets the optimization considered at. + /// public DateTimeOffset? OptimizationConsideredAt { get; private init; } + /// + /// Initializes a new instance of the class. + /// public TestRunGroup( ITestSuite suite, bool isSystemRun, @@ -35,6 +59,9 @@ public TestRunGroup( OptimizationConsideredAt = null; } + /// + /// Initializes a new instance of the class. + /// public TestRunGroup( ITestSuite suite, TestRunStatus status, @@ -57,12 +84,21 @@ public TestRunGroup( OptimizationConsideredAt = optimizationConsideredAt; } + /// + /// Mark optimization considered. + /// public Task MarkOptimizationConsidered(CancellationToken cancellationToken = default) => ApplyAsync(this with { OptimizationConsideredAt = DateTimeOffset.UtcNow }, cancellationToken); + /// + /// Gets the test runs. + /// public Task> GetTestRuns(CancellationToken cancellationToken = default) => testRuns.GetByGroupAsync(Id, cancellationToken); + /// + /// Validates. + /// public override IEnumerable Validate(ValidationContext validationContext) { foreach (var result in base.Validate(validationContext)) @@ -79,15 +115,27 @@ public override IEnumerable Validate(ValidationContext validat } } + /// + /// Sets the running. + /// public Task SetRunning(CancellationToken cancellationToken = default) => SetState(TestRunStatus.Running, cancellationToken); + /// + /// Sets the completed. + /// public Task SetCompleted(CancellationToken cancellationToken = default) => SetState(TestRunStatus.Completed, cancellationToken); + /// + /// Sets the failed. + /// public Task SetFailed(CancellationToken cancellationToken = default) => SetState(TestRunStatus.Failed, cancellationToken); + /// + /// Sets the cancelled. + /// public Task SetCancelled(CancellationToken cancellationToken = default) => SetState(TestRunStatus.Cancelled, cancellationToken); diff --git a/Proxytrace.Domain/TestRunGroup/Internal/TestRunGroupGenerator.cs b/Proxytrace.Domain/TestRunGroup/Internal/TestRunGroupGenerator.cs index f010b0185..185d213eb 100644 --- a/Proxytrace.Domain/TestRunGroup/Internal/TestRunGroupGenerator.cs +++ b/Proxytrace.Domain/TestRunGroup/Internal/TestRunGroupGenerator.cs @@ -9,6 +9,9 @@ internal class TestRunGroupGenerator : DomainEntityGenerator private readonly ITestRunGroup.CreateNew factory; private readonly IDomainEntityGenerator suiteGenerator; + /// + /// Initializes a new instance of the class. + /// public TestRunGroupGenerator( ITestRunGroup.CreateNew factory, IRepository repository, @@ -19,6 +22,9 @@ public TestRunGroupGenerator( this.suiteGenerator = suiteGenerator; } + /// + /// Generates asynchronously. + /// public override async Task GenerateAsync(CancellationToken cancellationToken = default) { var suite = await suiteGenerator.GetOrCreateAsync(cancellationToken); diff --git a/Proxytrace.Domain/TestRunSchedule/ITestRunSchedule.cs b/Proxytrace.Domain/TestRunSchedule/ITestRunSchedule.cs index 7a4910b8e..7346c655f 100644 --- a/Proxytrace.Domain/TestRunSchedule/ITestRunSchedule.cs +++ b/Proxytrace.Domain/TestRunSchedule/ITestRunSchedule.cs @@ -26,10 +26,16 @@ public interface ITestRunSchedule : IDomainEntity DateTimeOffset NextRunAt { get; } DateTimeOffset? LastRunAt { get; } + /// + /// Factory delegate for creating a new new instance. + /// public delegate ITestRunSchedule CreateNew( string name, ITestSuite suite, IReadOnlyCollection endpoints, TimeSpan interval, bool isEnabled, DateTimeOffset anchorAt); + /// + /// Factory delegate for creating a new existing instance. + /// public delegate ITestRunSchedule CreateExisting( string name, ITestSuite suite, IReadOnlyCollection endpoints, TimeSpan interval, bool isEnabled, DateTimeOffset anchorAt, DateTimeOffset nextRunAt, diff --git a/Proxytrace.Domain/TestRunSchedule/ITestRunScheduleRepository.cs b/Proxytrace.Domain/TestRunSchedule/ITestRunScheduleRepository.cs index 21b238298..c17346cdd 100644 --- a/Proxytrace.Domain/TestRunSchedule/ITestRunScheduleRepository.cs +++ b/Proxytrace.Domain/TestRunSchedule/ITestRunScheduleRepository.cs @@ -1,5 +1,8 @@ namespace Proxytrace.Domain.TestRunSchedule; +/// +/// Repository for persisting and querying test run schedule entities. +/// public interface ITestRunScheduleRepository : IRepository { Task> GetByAgentAsync(Guid agentId, CancellationToken cancellationToken = default); diff --git a/Proxytrace.Domain/TestRunSchedule/Internal/TestRunSchedule.cs b/Proxytrace.Domain/TestRunSchedule/Internal/TestRunSchedule.cs index a95723807..843c3df9c 100644 --- a/Proxytrace.Domain/TestRunSchedule/Internal/TestRunSchedule.cs +++ b/Proxytrace.Domain/TestRunSchedule/Internal/TestRunSchedule.cs @@ -9,15 +9,42 @@ namespace Proxytrace.Domain.TestRunSchedule.Internal; internal record TestRunSchedule : DomainEntity, ITestRunSchedule { + /// + /// Gets or sets the name. + /// public string Name { get; private init; } + /// + /// Gets or sets the suite. + /// public ITestSuite Suite { get; private init; } + /// + /// Gets or sets the endpoints. + /// public IReadOnlyCollection Endpoints { get; private init; } + /// + /// Gets or sets the interval. + /// public TimeSpan Interval { get; private init; } + /// + /// Gets or sets the is enabled. + /// public bool IsEnabled { get; private init; } + /// + /// Gets or sets the anchor at. + /// public DateTimeOffset AnchorAt { get; private init; } + /// + /// Gets or sets the next run at. + /// public DateTimeOffset NextRunAt { get; private init; } + /// + /// Gets or sets the last run at. + /// public DateTimeOffset? LastRunAt { get; private init; } + /// + /// Initializes a new instance of the class. + /// public TestRunSchedule( string name, ITestSuite suite, IReadOnlyCollection endpoints, TimeSpan interval, bool isEnabled, DateTimeOffset anchorAt, @@ -34,6 +61,9 @@ public TestRunSchedule( LastRunAt = null; } + /// + /// Initializes a new instance of the class. + /// public TestRunSchedule( string name, ITestSuite suite, IReadOnlyCollection endpoints, TimeSpan interval, bool isEnabled, DateTimeOffset anchorAt, DateTimeOffset nextRunAt, @@ -70,12 +100,21 @@ private static DateTimeOffset AlignForward(DateTimeOffset anchor, TimeSpan inter return anchor + TimeSpan.FromTicks(interval.Ticks * steps); } + /// + /// Disables. + /// public Task Disable(CancellationToken cancellationToken = default) => ApplyAsync(this with { IsEnabled = false }, cancellationToken); + /// + /// Enables. + /// public Task Enable(CancellationToken cancellationToken = default) => ApplyAsync(this with { IsEnabled = true }, cancellationToken); + /// + /// Record fired. + /// public Task RecordFired(DateTimeOffset now, CancellationToken cancellationToken = default) => ApplyAsync(this with { @@ -83,6 +122,9 @@ public Task RecordFired(DateTimeOffset now, CancellationToken NextRunAt = AlignForward(AnchorAt, Interval, now), }, cancellationToken); + /// + /// Updates. + /// public Task Update( string name, IReadOnlyCollection endpoints, TimeSpan interval, bool isEnabled, DateTimeOffset anchorAt, DateTimeOffset now, @@ -102,6 +144,9 @@ public Task Update( : NextRunAt, }, cancellationToken); + /// + /// Validates. + /// public override IEnumerable Validate(ValidationContext validationContext) { foreach (var result in base.Validate(validationContext)) diff --git a/Proxytrace.Domain/TestRunSchedule/Internal/TestRunScheduleGenerator.cs b/Proxytrace.Domain/TestRunSchedule/Internal/TestRunScheduleGenerator.cs index be1bbbcb1..ba33cd26f 100644 --- a/Proxytrace.Domain/TestRunSchedule/Internal/TestRunScheduleGenerator.cs +++ b/Proxytrace.Domain/TestRunSchedule/Internal/TestRunScheduleGenerator.cs @@ -16,6 +16,9 @@ internal class TestRunScheduleGenerator : DomainEntityGenerator + /// Initializes a new instance of the class. + /// public TestRunScheduleGenerator( ITestRunSchedule.CreateNew factory, IRepository repository, @@ -28,6 +31,9 @@ public TestRunScheduleGenerator( this.endpointGenerator = endpointGenerator; } + /// + /// Generates asynchronously. + /// public override async Task GenerateAsync(CancellationToken cancellationToken = default) { var suite = await suiteGenerator.GetOrCreateAsync(cancellationToken); diff --git a/Proxytrace.Domain/TestSuite/Internal/TestSuite.cs b/Proxytrace.Domain/TestSuite/Internal/TestSuite.cs index 842bda539..bb27793ac 100644 --- a/Proxytrace.Domain/TestSuite/Internal/TestSuite.cs +++ b/Proxytrace.Domain/TestSuite/Internal/TestSuite.cs @@ -10,12 +10,30 @@ namespace Proxytrace.Domain.TestSuite.Internal; internal record TestSuite : DomainEntity, ITestSuite { + /// + /// Gets the name. + /// public string Name { get; } + /// + /// Gets the agent. + /// public IAgent Agent { get; } + /// + /// Gets the evaluators. + /// public IReadOnlyCollection Evaluators { get; } + /// + /// Gets the test cases. + /// public IReadOnlyCollection TestCases { get; } + /// + /// Gets the project. + /// public IProject Project => Agent.Project; + /// + /// Initializes a new instance of the class. + /// public TestSuite( string name, IAgent agent, @@ -29,6 +47,9 @@ public TestSuite( TestCases = testCases.ToArray(); } + /// + /// Initializes a new instance of the class. + /// public TestSuite( string name, IAgent agent, @@ -43,6 +64,9 @@ public TestSuite( TestCases = testCases.ToArray(); } + /// + /// Validates. + /// public override IEnumerable Validate(ValidationContext validationContext) { foreach (var result in base.Validate(validationContext)) diff --git a/Proxytrace.Domain/TestSuite/Internal/TestSuiteGenerator.cs b/Proxytrace.Domain/TestSuite/Internal/TestSuiteGenerator.cs index 20d39d37a..c2ce98465 100644 --- a/Proxytrace.Domain/TestSuite/Internal/TestSuiteGenerator.cs +++ b/Proxytrace.Domain/TestSuite/Internal/TestSuiteGenerator.cs @@ -19,6 +19,9 @@ internal class TestSuiteGenerator : DomainEntityGenerator "Error Handling", "Integration", "Boundary Tests", "Sanity Check", "Full Coverage" ]; + /// + /// Initializes a new instance of the class. + /// public TestSuiteGenerator( ITestSuite.CreateNew factory, IRepository repository, @@ -33,6 +36,9 @@ public TestSuiteGenerator( this.testCaseGenerator = testCaseGenerator; } + /// + /// Generates asynchronously. + /// public override async Task GenerateAsync(CancellationToken cancellationToken = default) { var agent = await agentGenerator.GetOrCreateAsync(cancellationToken); diff --git a/Proxytrace.Domain/User/IUser.cs b/Proxytrace.Domain/User/IUser.cs index e71af40c9..e6f789cb6 100644 --- a/Proxytrace.Domain/User/IUser.cs +++ b/Proxytrace.Domain/User/IUser.cs @@ -49,6 +49,12 @@ public interface IUser : IDomainEntity /// Updates the user's email notification preferences and persists. Task ChangeEmailNotificationPreferences(bool emailNotificationsEnabled, NotificationSeverity emailNotificationMinSeverity, CancellationToken cancellationToken = default); + /// + /// Factory delegate for creating a new new instance. + /// public delegate IUser CreateNew(string email, string? externalSubject, string? passwordHash, UserRole role, string language = SupportedLanguages.Default, bool emailNotificationsEnabled = true, NotificationSeverity emailNotificationMinSeverity = NotificationSeverity.Info); + /// + /// Factory delegate for creating a new existing instance. + /// public delegate IUser CreateExisting(string email, string? externalSubject, string? passwordHash, UserRole role, string language, bool emailNotificationsEnabled, NotificationSeverity emailNotificationMinSeverity, IDomainEntityData existing); } diff --git a/Proxytrace.Domain/User/IUserRepository.cs b/Proxytrace.Domain/User/IUserRepository.cs index aa3e7b86f..5abc8c47b 100644 --- a/Proxytrace.Domain/User/IUserRepository.cs +++ b/Proxytrace.Domain/User/IUserRepository.cs @@ -1,5 +1,8 @@ namespace Proxytrace.Domain.User; +/// +/// Repository for persisting and querying user entities. +/// public interface IUserRepository : IRepository { Task FindByExternalSubjectAsync(string externalSubject, CancellationToken cancellationToken = default); diff --git a/Proxytrace.Domain/User/Internal/User.cs b/Proxytrace.Domain/User/Internal/User.cs index e49229a70..6328e2234 100644 --- a/Proxytrace.Domain/User/Internal/User.cs +++ b/Proxytrace.Domain/User/Internal/User.cs @@ -8,14 +8,38 @@ namespace Proxytrace.Domain.User.Internal; internal record User : DomainEntity, IUser { + /// + /// Gets the email. + /// public string Email { get; } + /// + /// Gets the external subject. + /// public string? ExternalSubject { get; } + /// + /// Gets or sets the password hash. + /// public string? PasswordHash { get; private init; } + /// + /// Gets or sets the role. + /// public UserRole Role { get; private init; } + /// + /// Gets or sets the language. + /// public string Language { get; private init; } + /// + /// Gets or sets the email notifications enabled. + /// public bool EmailNotificationsEnabled { get; private init; } + /// + /// Gets or sets the email notification min severity. + /// public NotificationSeverity EmailNotificationMinSeverity { get; private init; } + /// + /// Initializes a new instance of the class. + /// public User( string email, string? externalSubject, @@ -38,6 +62,9 @@ public User( EmailNotificationMinSeverity = emailNotificationMinSeverity; } + /// + /// Initializes a new instance of the class. + /// public User( string email, string? externalSubject, @@ -60,19 +87,31 @@ public User( EmailNotificationMinSeverity = emailNotificationMinSeverity; } + /// + /// Change role. + /// public Task ChangeRole(UserRole role, CancellationToken cancellationToken = default) => Role == role ? Task.FromResult(this) : ApplyAsync(this with { Role = role }, cancellationToken); + /// + /// Change password hash. + /// public Task ChangePasswordHash(string passwordHash, CancellationToken cancellationToken = default) => ApplyAsync(this with { PasswordHash = passwordHash }, cancellationToken); + /// + /// Change language. + /// public Task ChangeLanguage(string language, CancellationToken cancellationToken = default) => Language == language ? Task.FromResult(this) : ApplyAsync(this with { Language = language }, cancellationToken); + /// + /// Change email notification preferences. + /// public Task ChangeEmailNotificationPreferences(bool emailNotificationsEnabled, NotificationSeverity emailNotificationMinSeverity, CancellationToken cancellationToken = default) => EmailNotificationsEnabled == emailNotificationsEnabled && EmailNotificationMinSeverity == emailNotificationMinSeverity ? Task.FromResult(this) @@ -103,6 +142,9 @@ protected override bool PrintMembers(StringBuilder builder) return true; } + /// + /// Validates. + /// public override IEnumerable Validate(ValidationContext validationContext) { foreach (var result in base.Validate(validationContext)) diff --git a/Proxytrace.Domain/User/Internal/UserGenerator.cs b/Proxytrace.Domain/User/Internal/UserGenerator.cs index e4286aff1..adb78bbab 100644 --- a/Proxytrace.Domain/User/Internal/UserGenerator.cs +++ b/Proxytrace.Domain/User/Internal/UserGenerator.cs @@ -9,6 +9,9 @@ internal class UserGenerator : DomainEntityGenerator { private readonly IUser.CreateNew factory; + /// + /// Initializes a new instance of the class. + /// public UserGenerator( IUser.CreateNew factory, IRepository repository, @@ -17,6 +20,9 @@ public UserGenerator( this.factory = factory; } + /// + /// Generates asynchronously. + /// public override Task GenerateAsync(CancellationToken cancellationToken = default) => factory( email: random.Email(), diff --git a/Proxytrace.Domain/UserTotpEnrollment/IUserTotpEnrollment.cs b/Proxytrace.Domain/UserTotpEnrollment/IUserTotpEnrollment.cs index 6d056d561..df60f1f8d 100644 --- a/Proxytrace.Domain/UserTotpEnrollment/IUserTotpEnrollment.cs +++ b/Proxytrace.Domain/UserTotpEnrollment/IUserTotpEnrollment.cs @@ -39,6 +39,12 @@ public interface IUserTotpEnrollment : IDomainEntity /// Records the time-step of an accepted code (replay guard) on a confirmed enrollment. Persists. Task RecordUsedStep(long step, CancellationToken cancellationToken = default); + /// + /// Factory delegate for creating a new new instance. + /// public delegate IUserTotpEnrollment CreateNew(IUser user, string secret); + /// + /// Factory delegate for creating a new existing instance. + /// public delegate IUserTotpEnrollment CreateExisting(IUser user, string secret, DateTimeOffset? confirmedAt, long? lastUsedStep, IDomainEntityData existing); } diff --git a/Proxytrace.Domain/UserTotpEnrollment/IUserTotpEnrollmentRepository.cs b/Proxytrace.Domain/UserTotpEnrollment/IUserTotpEnrollmentRepository.cs index 3a4aca201..c085b4bab 100644 --- a/Proxytrace.Domain/UserTotpEnrollment/IUserTotpEnrollmentRepository.cs +++ b/Proxytrace.Domain/UserTotpEnrollment/IUserTotpEnrollmentRepository.cs @@ -1,5 +1,8 @@ namespace Proxytrace.Domain.UserTotpEnrollment; +/// +/// Repository for persisting and querying user totp enrollment entities. +/// public interface IUserTotpEnrollmentRepository : IRepository { /// The user's TOTP enrollment (pending or confirmed), or if none exists. diff --git a/Proxytrace.Domain/UserTotpEnrollment/Internal/UserTotpEnrollment.cs b/Proxytrace.Domain/UserTotpEnrollment/Internal/UserTotpEnrollment.cs index c4b16d8d7..09d6eb392 100644 --- a/Proxytrace.Domain/UserTotpEnrollment/Internal/UserTotpEnrollment.cs +++ b/Proxytrace.Domain/UserTotpEnrollment/Internal/UserTotpEnrollment.cs @@ -8,11 +8,26 @@ namespace Proxytrace.Domain.UserTotpEnrollment.Internal; internal record UserTotpEnrollment : DomainEntity, IUserTotpEnrollment { + /// + /// Gets the user. + /// public IUser User { get; } + /// + /// Gets the secret. + /// public string Secret { get; } + /// + /// Gets or sets the confirmed at. + /// public DateTimeOffset? ConfirmedAt { get; private init; } + /// + /// Gets or sets the last used step. + /// public long? LastUsedStep { get; private init; } + /// + /// Initializes a new instance of the class. + /// public UserTotpEnrollment( IUser user, string secret, @@ -22,6 +37,9 @@ public UserTotpEnrollment( Secret = secret; } + /// + /// Initializes a new instance of the class. + /// public UserTotpEnrollment( IUser user, string secret, @@ -36,9 +54,15 @@ public UserTotpEnrollment( LastUsedStep = lastUsedStep; } + /// + /// Confirm. + /// public Task Confirm(long usedStep, CancellationToken cancellationToken = default) => ApplyAsync(this with { ConfirmedAt = DateTimeOffset.UtcNow, LastUsedStep = usedStep }, cancellationToken); + /// + /// Record used step. + /// public Task RecordUsedStep(long step, CancellationToken cancellationToken = default) { // Single-use guard: a TOTP step may only ever move forward, so replaying a step at or @@ -70,6 +94,9 @@ protected override bool PrintMembers(StringBuilder builder) return true; } + /// + /// Validates. + /// public override IEnumerable Validate(ValidationContext validationContext) { foreach (var result in base.Validate(validationContext)) diff --git a/Proxytrace.Domain/UserTotpEnrollment/Internal/UserTotpEnrollmentGenerator.cs b/Proxytrace.Domain/UserTotpEnrollment/Internal/UserTotpEnrollmentGenerator.cs index 659a7e904..bc3c0e2d6 100644 --- a/Proxytrace.Domain/UserTotpEnrollment/Internal/UserTotpEnrollmentGenerator.cs +++ b/Proxytrace.Domain/UserTotpEnrollment/Internal/UserTotpEnrollmentGenerator.cs @@ -9,6 +9,9 @@ internal class UserTotpEnrollmentGenerator : DomainEntityGenerator users; + /// + /// Initializes a new instance of the class. + /// public UserTotpEnrollmentGenerator( IUserTotpEnrollment.CreateNew factory, IDomainEntityGenerator users, @@ -19,6 +22,9 @@ public UserTotpEnrollmentGenerator( this.users = users; } + /// + /// Generates asynchronously. + /// public override async Task GenerateAsync(CancellationToken cancellationToken = default) { var user = await users.GetOrCreateAsync(cancellationToken); diff --git a/Proxytrace.Infrastructure/Internal/ChatClientExtensions.cs b/Proxytrace.Infrastructure/Internal/ChatClientExtensions.cs index 4dd6c49a0..0bbf9f450 100644 --- a/Proxytrace.Infrastructure/Internal/ChatClientExtensions.cs +++ b/Proxytrace.Infrastructure/Internal/ChatClientExtensions.cs @@ -13,9 +13,15 @@ namespace Proxytrace.Infrastructure.Internal; internal static class ChatClientExtensions { + /// + /// To chat messages. + /// public static IEnumerable ToChatMessages(this Conversation conversation) => conversation.Messages.Select(m => m.ToChatMessage()); + /// + /// To chat message. + /// public static ChatMessage ToChatMessage(this Message message) { ChatRole role = message.Role switch @@ -63,6 +69,9 @@ private static string BuildText(IReadOnlyList contents) return sb.ToString().Trim(); } + /// + /// To chat options. + /// public static ChatOptions ToChatOptions(this ModelOptions options) { var chatOptions = new ChatOptions { ModelId = options.ModelName }; diff --git a/Proxytrace.Infrastructure/Internal/FrankfurterFxRateProvider.cs b/Proxytrace.Infrastructure/Internal/FrankfurterFxRateProvider.cs index 67bcc9221..94d15387e 100644 --- a/Proxytrace.Infrastructure/Internal/FrankfurterFxRateProvider.cs +++ b/Proxytrace.Infrastructure/Internal/FrankfurterFxRateProvider.cs @@ -37,6 +37,9 @@ internal sealed class FrankfurterFxRateProvider : IFxRateProvider // Interlocked to keep the read atomic on 32-bit runtimes. private long retryNotBeforeTicks; + /// + /// Initializes a new instance of the class. + /// public FrankfurterFxRateProvider(HttpClient http, PricingOptions options, IAsyncLock asyncLock, IClock clock) { this.http = http; @@ -45,6 +48,9 @@ public FrankfurterFxRateProvider(HttpClient http, PricingOptions options, IAsync this.clock = clock; } + /// + /// Gets the usd to eur asynchronously. + /// public async Task GetUsdToEurAsync(CancellationToken cancellationToken = default) { DateOnly today = DateOnly.FromDateTime(clock.UtcNow.UtcDateTime); diff --git a/Proxytrace.Infrastructure/Internal/LiteLlmCatalogResolver.cs b/Proxytrace.Infrastructure/Internal/LiteLlmCatalogResolver.cs index c2b6577c8..bc4f7e889 100644 --- a/Proxytrace.Infrastructure/Internal/LiteLlmCatalogResolver.cs +++ b/Proxytrace.Infrastructure/Internal/LiteLlmCatalogResolver.cs @@ -40,6 +40,9 @@ internal sealed class LiteLlmCatalogResolver // Interlocked to keep the read atomic on 32-bit runtimes. private long retryNotBeforeTicks; + /// + /// Initializes a new instance of the class. + /// public LiteLlmCatalogResolver( HttpClient http, PricingOptions options, diff --git a/Proxytrace.Infrastructure/Internal/ModelClient.cs b/Proxytrace.Infrastructure/Internal/ModelClient.cs index 3e1ae2bd1..286e51ba3 100644 --- a/Proxytrace.Infrastructure/Internal/ModelClient.cs +++ b/Proxytrace.Infrastructure/Internal/ModelClient.cs @@ -32,6 +32,9 @@ internal class ModelClient : IModelClient private readonly IChatClient chatClient; private bool disposed; + /// + /// Initializes a new instance of the class. + /// public ModelClient( IAgent agent, IModelEndpoint? customEndpoint, @@ -77,6 +80,9 @@ internal ModelClient( // owns the IChatClient it was built with — the OpenAI-backed transport behind it is disposable. // Disposing here releases that transport instead of abandoning it. Idempotent so the deterministic // using-disposal at the call site and any Autofac scope-end disposal can't double-free. + /// + /// Releases all resources used by the current instance. + /// public void Dispose() { if (disposed) @@ -88,6 +94,9 @@ public void Dispose() chatClient.Dispose(); } + /// + /// Completes asynchronously. + /// public Task CompleteAsync( Conversation conversation, ModelOptions? options = null, @@ -98,6 +107,9 @@ public Task CompleteAsync( return CompleteAsync(systemMessage, conversation, options, cancellationToken); } + /// + /// Completes asynchronously. + /// public async Task> CompleteAsync( Conversation conversation, ModelOptions? options = null, @@ -126,6 +138,9 @@ public async Task> CompleteAsync( return new TypedCompletion(output, completion.Usage, completion.Latency); } + /// + /// Builds the request preview. + /// public ModelRequestPreview BuildRequestPreview( Conversation conversation, ModelOptions? options = null, @@ -301,6 +316,9 @@ private static bool RejectsReasoningEffort(ClientResultException error, ModelSam && error.Status == (int)HttpStatusCode.BadRequest && error.Message.Contains("reasoning", StringComparison.OrdinalIgnoreCase); + /// + /// Stream asynchronously. + /// public async IAsyncEnumerable StreamAsync( SystemMessage systemMessage, Conversation conversation, diff --git a/Proxytrace.Infrastructure/Internal/PricingOptions.cs b/Proxytrace.Infrastructure/Internal/PricingOptions.cs index 510a8a3fa..b2c8c35c1 100644 --- a/Proxytrace.Infrastructure/Internal/PricingOptions.cs +++ b/Proxytrace.Infrastructure/Internal/PricingOptions.cs @@ -3,7 +3,13 @@ namespace Proxytrace.Infrastructure.Internal; /// Pricing feed endpoints. Defaults are baked in; override via the "Pricing" config section. public sealed class PricingOptions { + /// + /// Gets or sets the lite llm feed url. + /// public string LiteLlmFeedUrl { get; init; } = "https://raw.githubusercontent.com/BerriAI/litellm/main/model_prices_and_context_window.json"; + /// + /// Gets or sets the fx api url. + /// public string FxApiUrl { get; init; } = "https://api.frankfurter.app/latest"; } diff --git a/Proxytrace.Infrastructure/Internal/PricingService.cs b/Proxytrace.Infrastructure/Internal/PricingService.cs index dc1166c50..c302c432a 100644 --- a/Proxytrace.Infrastructure/Internal/PricingService.cs +++ b/Proxytrace.Infrastructure/Internal/PricingService.cs @@ -11,11 +11,17 @@ internal sealed class PricingService : IPricingService { private readonly LiteLlmCatalogResolver liteLlmResolver; + /// + /// Initializes a new instance of the class. + /// public PricingService(LiteLlmCatalogResolver liteLlmResolver) { this.liteLlmResolver = liteLlmResolver; } + /// + /// Resolves asynchronously. + /// public Task ResolveAsync( IModelProvider provider, DiscoveredModel model, diff --git a/Proxytrace.Infrastructure/Internal/ProviderClient.cs b/Proxytrace.Infrastructure/Internal/ProviderClient.cs index ece7c3140..46ece1d83 100644 --- a/Proxytrace.Infrastructure/Internal/ProviderClient.cs +++ b/Proxytrace.Infrastructure/Internal/ProviderClient.cs @@ -18,6 +18,9 @@ internal sealed class ProviderClient : IProviderClient private readonly HttpClient http; private readonly IPricingService pricingService; + /// + /// Initializes a new instance of the class. + /// public ProviderClient( IModelProvider provider, IModelRepository modelRepository, @@ -30,6 +33,9 @@ public ProviderClient( this.pricingService = pricingService; } + /// + /// Verifies the connection asynchronously. + /// public async Task VerifyConnectionAsync(CancellationToken cancellationToken = default) { try @@ -56,6 +62,9 @@ public async Task VerifyConnectionAsync(CancellationTo } } + /// + /// Gets the models asynchronously. + /// public async Task> GetModelsAsync(CancellationToken cancellationToken = default) { EnsureSupportedKind(); diff --git a/Proxytrace.Infrastructure/Module.cs b/Proxytrace.Infrastructure/Module.cs index 5d6ba7ceb..1f30612ce 100644 --- a/Proxytrace.Infrastructure/Module.cs +++ b/Proxytrace.Infrastructure/Module.cs @@ -9,6 +9,9 @@ namespace Proxytrace.Infrastructure; +/// +/// Autofac module that registers services. +/// public class Module : Autofac.Module { protected override void Load(ContainerBuilder builder) diff --git a/Proxytrace.Infrastructure/Security/Internal/BlindIndexKey.cs b/Proxytrace.Infrastructure/Security/Internal/BlindIndexKey.cs index 28a25adb2..9bbbec5e2 100644 --- a/Proxytrace.Infrastructure/Security/Internal/BlindIndexKey.cs +++ b/Proxytrace.Infrastructure/Security/Internal/BlindIndexKey.cs @@ -32,6 +32,9 @@ internal sealed class BlindIndexKey private const string KeyFileName = "blind-index.key"; private const int KeyLengthBytes = 32; + /// + /// Initializes a new instance of the class. + /// public BlindIndexKey(KeyRingLocation location, ILogger logger) { if (location.KeyRingPath is not { } keyRingPath) @@ -61,6 +64,9 @@ public BlindIndexKey(KeyRingLocation location, ILogger logger) /// The key bytes, or when no persisted key is available. public byte[]? Material { get; } + /// + /// Gets the is available. + /// public bool IsAvailable => Material is not null; private static byte[] LoadOrCreate(string path) diff --git a/Proxytrace.Infrastructure/Security/Internal/DataProtectionSecretProtector.cs b/Proxytrace.Infrastructure/Security/Internal/DataProtectionSecretProtector.cs index 9102129ee..08ec6cbaa 100644 --- a/Proxytrace.Infrastructure/Security/Internal/DataProtectionSecretProtector.cs +++ b/Proxytrace.Infrastructure/Security/Internal/DataProtectionSecretProtector.cs @@ -7,12 +7,21 @@ internal sealed class DataProtectionSecretProtector : ISecretProtector { private readonly IDataProtector protector; + /// + /// Initializes a new instance of the class. + /// public DataProtectionSecretProtector(IDataProtectionProvider provider) { protector = provider.CreateProtector("Proxytrace.Secrets.v1"); } + /// + /// Protect. + /// public string Protect(string plaintext) => protector.Protect(plaintext); + /// + /// Unprotect. + /// public string Unprotect(string ciphertext) => protector.Unprotect(ciphertext); } diff --git a/Proxytrace.Infrastructure/Security/Internal/HmacSecretIndexer.cs b/Proxytrace.Infrastructure/Security/Internal/HmacSecretIndexer.cs index 5f7bcc5e0..c4ea3773f 100644 --- a/Proxytrace.Infrastructure/Security/Internal/HmacSecretIndexer.cs +++ b/Proxytrace.Infrastructure/Security/Internal/HmacSecretIndexer.cs @@ -13,6 +13,9 @@ internal sealed class HmacSecretIndexer : ISecretIndexer { private readonly BlindIndexKey key; + /// + /// Initializes a new instance of the class. + /// public HmacSecretIndexer(BlindIndexKey key, ILogger logger) { this.key = key; @@ -27,13 +30,22 @@ public HmacSecretIndexer(BlindIndexKey key, ILogger logger) } } + /// + /// Gets the is keyed. + /// public bool IsKeyed => key.IsAvailable; + /// + /// Indexes. + /// public string Index(string value) => key.Material is { } material ? SecretIndexScheme.KeyedPrefix + Convert.ToHexString( HMACSHA256.HashData(material, Encoding.UTF8.GetBytes(value))).ToLowerInvariant() : LegacyIndex(value); + /// + /// Legacy index. + /// public string LegacyIndex(string value) => Sha256.HexHash(value); } diff --git a/Proxytrace.Infrastructure/Security/Internal/Sha256SecretHasher.cs b/Proxytrace.Infrastructure/Security/Internal/Sha256SecretHasher.cs index 4e67af7f6..f821737d1 100644 --- a/Proxytrace.Infrastructure/Security/Internal/Sha256SecretHasher.cs +++ b/Proxytrace.Infrastructure/Security/Internal/Sha256SecretHasher.cs @@ -4,5 +4,8 @@ namespace Proxytrace.Infrastructure.Security.Internal; internal sealed class Sha256SecretHasher : ISecretHasher { + /// + /// Hashes. + /// public string Hash(string value) => Sha256.HexHash(value); } diff --git a/Proxytrace.Infrastructure/Security/SecretProtectionModule.cs b/Proxytrace.Infrastructure/Security/SecretProtectionModule.cs index f7d9affe7..b9eec97d7 100644 --- a/Proxytrace.Infrastructure/Security/SecretProtectionModule.cs +++ b/Proxytrace.Infrastructure/Security/SecretProtectionModule.cs @@ -113,12 +113,18 @@ internal sealed class KeyRingPersistenceCheck : IHostedService // proxy an ILogger whose T is internal, which would make this unassertable in a test. private readonly ILogger logger; + /// + /// Initializes a new instance of the class. + /// public KeyRingPersistenceCheck(KeyRingLocation location, ILogger logger) { this.location = location; this.logger = logger; } + /// + /// Starts asynchronously. + /// public Task StartAsync(CancellationToken cancellationToken) { if (location.KeyRingPath is null) @@ -142,5 +148,8 @@ public Task StartAsync(CancellationToken cancellationToken) return Task.CompletedTask; } + /// + /// Stops asynchronously. + /// public Task StopAsync(CancellationToken cancellationToken) => Task.CompletedTask; } diff --git a/Proxytrace.Licensing/Exceptions/FeatureNotLicensedException.cs b/Proxytrace.Licensing/Exceptions/FeatureNotLicensedException.cs index c89670215..5df7be4ac 100644 --- a/Proxytrace.Licensing/Exceptions/FeatureNotLicensedException.cs +++ b/Proxytrace.Licensing/Exceptions/FeatureNotLicensedException.cs @@ -6,6 +6,9 @@ namespace Proxytrace.Licensing.Exceptions; /// public sealed class FeatureNotLicensedException : Exception { + /// + /// Initializes a new instance of the class. + /// public FeatureNotLicensedException(LicenseFeature feature, LicenseTier tier) : base($"The feature '{feature}' is not available on the '{tier}' tier.") { diff --git a/Proxytrace.Licensing/Exceptions/InvalidLicenseException.cs b/Proxytrace.Licensing/Exceptions/InvalidLicenseException.cs index e8eb514fd..f50a66273 100644 --- a/Proxytrace.Licensing/Exceptions/InvalidLicenseException.cs +++ b/Proxytrace.Licensing/Exceptions/InvalidLicenseException.cs @@ -19,17 +19,26 @@ public enum InvalidLicenseReason /// public sealed class InvalidLicenseException : Exception { + /// + /// Initializes a new instance of the class. + /// public InvalidLicenseException(InvalidLicenseReason reason) : this(reason, $"The configured license is invalid: {reason}.") { } + /// + /// Initializes a new instance of the class. + /// public InvalidLicenseException(InvalidLicenseReason reason, string message) : base(message) { Reason = reason; } + /// + /// Initializes a new instance of the class. + /// public InvalidLicenseException(InvalidLicenseReason reason, string message, Exception innerException) : base(message, innerException) { diff --git a/Proxytrace.Licensing/Exceptions/LicenseLimitExceededException.cs b/Proxytrace.Licensing/Exceptions/LicenseLimitExceededException.cs index 6fab18768..8837b264c 100644 --- a/Proxytrace.Licensing/Exceptions/LicenseLimitExceededException.cs +++ b/Proxytrace.Licensing/Exceptions/LicenseLimitExceededException.cs @@ -6,6 +6,9 @@ namespace Proxytrace.Licensing.Exceptions; /// public sealed class LicenseLimitExceededException : Exception { + /// + /// Initializes a new instance of the class. + /// public LicenseLimitExceededException(LicenseLimit limit, long current, long max) : base($"The limit '{limit}' has been reached ({current}/{max}).") { diff --git a/Proxytrace.Licensing/Internal/LicenseActivatorAdapter.cs b/Proxytrace.Licensing/Internal/LicenseActivatorAdapter.cs index ff3586cf2..52a6d16fa 100644 --- a/Proxytrace.Licensing/Internal/LicenseActivatorAdapter.cs +++ b/Proxytrace.Licensing/Internal/LicenseActivatorAdapter.cs @@ -12,22 +12,37 @@ internal sealed class LicenseActivatorAdapter : ILicenseActivator { private readonly Core.ILicenseActivator engine; + /// + /// Initializes a new instance of the class. + /// public LicenseActivatorAdapter(Core.ILicenseActivator engine) { ArgumentNullException.ThrowIfNull(engine); this.engine = engine; } + /// + /// Validates. + /// public LicenseSnapshot Validate(string licenseJwt) => Guarded(() => engine.Validate(licenseJwt)); + /// + /// Activate. + /// public LicenseSnapshot Activate(string licenseJwt, LicenseSource source) => Guarded(() => engine.Activate(licenseJwt, LicenseSnapshotMapper.ToCore(source))); + /// + /// Activate or invalid. + /// public LicenseSnapshot ActivateOrInvalid(string licenseJwt, LicenseSource source) => LicenseSnapshotMapper.ToProduct( engine.ActivateOrInvalid(licenseJwt, LicenseSnapshotMapper.ToCore(source))); + /// + /// Activate configured. + /// public LicenseSnapshot ActivateConfigured() => LicenseSnapshotMapper.ToProduct(engine.ActivateConfigured()); diff --git a/Proxytrace.Licensing/Internal/LicenseServiceAdapter.cs b/Proxytrace.Licensing/Internal/LicenseServiceAdapter.cs index b0b475608..5f002fdb5 100644 --- a/Proxytrace.Licensing/Internal/LicenseServiceAdapter.cs +++ b/Proxytrace.Licensing/Internal/LicenseServiceAdapter.cs @@ -11,24 +11,42 @@ internal sealed class LicenseServiceAdapter : ILicenseService { private readonly Core.ILicenseService engine; + /// + /// Initializes a new instance of the class. + /// public LicenseServiceAdapter(Core.ILicenseService engine) { ArgumentNullException.ThrowIfNull(engine); this.engine = engine; } + /// + /// Gets the current. + /// public LicenseSnapshot Current => LicenseSnapshotMapper.ToProduct(engine.Current); + /// + /// Occurs when changed. + /// public event Action? Changed { add => engine.Changed += value; remove => engine.Changed -= value; } + /// + /// Determines whether the feature enabled. + /// public bool IsFeatureEnabled(LicenseFeature feature) => engine.HasFeature(feature.ToString()); + /// + /// Gets the limit. + /// public long GetLimit(LicenseLimit limit) => engine.GetLimit(limit.ToString()); + /// + /// Force refresh asynchronously. + /// public Task ForceRefreshAsync(CancellationToken cancellationToken = default) => engine.ForceRefreshAsync(cancellationToken); } diff --git a/Proxytrace.Licensing/Internal/LicenseSnapshotMapper.cs b/Proxytrace.Licensing/Internal/LicenseSnapshotMapper.cs index 13a771aad..cbfc90238 100644 --- a/Proxytrace.Licensing/Internal/LicenseSnapshotMapper.cs +++ b/Proxytrace.Licensing/Internal/LicenseSnapshotMapper.cs @@ -11,6 +11,9 @@ namespace Proxytrace.Licensing.Internal; /// internal static class LicenseSnapshotMapper { + /// + /// To product. + /// public static LicenseSnapshot ToProduct(Core.LicenseSnapshot snapshot) { var tier = Enum.TryParse(snapshot.Tier, ignoreCase: true, out var parsedTier) @@ -45,6 +48,9 @@ public static LicenseSnapshot ToProduct(Core.LicenseSnapshot snapshot) snapshot.Offline); } + /// + /// To core. + /// public static Core.LicenseSnapshot ToCore(LicenseSnapshot snapshot) => new( snapshot.Tier.ToString(), @@ -59,6 +65,9 @@ public static Core.LicenseSnapshot ToCore(LicenseSnapshot snapshot) snapshot.InvalidReason, snapshot.Offline); + /// + /// To product. + /// public static LicenseStatus ToProduct(Core.LicenseStatus status) => status switch { Core.LicenseStatus.Free => LicenseStatus.Free, @@ -69,6 +78,9 @@ public static Core.LicenseSnapshot ToCore(LicenseSnapshot snapshot) _ => LicenseStatus.Free, }; + /// + /// To core. + /// public static Core.LicenseStatus ToCore(LicenseStatus status) => status switch { LicenseStatus.Free => Core.LicenseStatus.Free, @@ -79,6 +91,9 @@ public static Core.LicenseSnapshot ToCore(LicenseSnapshot snapshot) _ => Core.LicenseStatus.Free, }; + /// + /// To product. + /// public static LicenseSource ToProduct(Core.LicenseSource source) => source switch { Core.LicenseSource.None => LicenseSource.None, @@ -88,6 +103,9 @@ public static Core.LicenseSnapshot ToCore(LicenseSnapshot snapshot) _ => LicenseSource.None, }; + /// + /// To core. + /// public static Core.LicenseSource ToCore(LicenseSource source) => source switch { LicenseSource.None => Core.LicenseSource.None, @@ -104,6 +122,9 @@ public static Core.LicenseSnapshot ToCore(LicenseSnapshot snapshot) public static InvalidLicenseException ToProduct(Core.InvalidLicenseException exception) => new(ToProduct(exception.Reason), exception.Message, exception); + /// + /// To product. + /// public static InvalidLicenseReason ToProduct(Core.InvalidLicenseReason reason) => reason switch { Core.InvalidLicenseReason.Malformed => InvalidLicenseReason.Malformed, diff --git a/Proxytrace.Licensing/Internal/ProxytraceLicenseTierPolicy.cs b/Proxytrace.Licensing/Internal/ProxytraceLicenseTierPolicy.cs index db2c9aac8..2623d603d 100644 --- a/Proxytrace.Licensing/Internal/ProxytraceLicenseTierPolicy.cs +++ b/Proxytrace.Licensing/Internal/ProxytraceLicenseTierPolicy.cs @@ -10,8 +10,14 @@ namespace Proxytrace.Licensing.Internal; /// internal sealed class ProxytraceLicenseTierPolicy : Core.ILicenseTierPolicy { + /// + /// Gets the fallback tier. + /// public string FallbackTier => nameof(LicenseTier.Free); + /// + /// Gets the definition. + /// public Core.TierDefinition GetDefinition(string tier) { var resolved = Enum.TryParse(tier, ignoreCase: true, out var parsed) @@ -24,12 +30,21 @@ public Core.TierDefinition GetDefinition(string tier) definition.Limits.ToDictionary(pair => pair.Key.ToString(), pair => pair.Value)); } + /// + /// Tries to the resolve tier. + /// public bool TryResolveTier(string? value, out string tier) => TryResolve(value, out tier); + /// + /// Tries to the resolve feature. + /// public bool TryResolveFeature(string value, out string feature) => TryResolve(value, out feature); + /// + /// Tries to the resolve limit. + /// public bool TryResolveLimit(string value, out string limit) => TryResolve(value, out limit); diff --git a/Proxytrace.Licensing/Module.cs b/Proxytrace.Licensing/Module.cs index 6d99f1783..f27d4ea49 100644 --- a/Proxytrace.Licensing/Module.cs +++ b/Proxytrace.Licensing/Module.cs @@ -27,6 +27,9 @@ public sealed class Module : Autofac.Module private readonly LicensingConfiguration configuration; + /// + /// Initializes a new instance of the class. + /// public Module(LicensingConfiguration configuration) { this.configuration = configuration ?? throw new ArgumentNullException(nameof(configuration)); diff --git a/Proxytrace.Messaging/Internal/InProcessIngestionStream.cs b/Proxytrace.Messaging/Internal/InProcessIngestionStream.cs index fa5fc1d3c..884f0f29a 100644 --- a/Proxytrace.Messaging/Internal/InProcessIngestionStream.cs +++ b/Proxytrace.Messaging/Internal/InProcessIngestionStream.cs @@ -21,6 +21,9 @@ internal sealed class InProcessIngestionStream : IIngestionStream // incremented on publish, decremented as each envelope is pulled by the consumer. private long depth; + /// + /// Publishes asynchronously. + /// public async Task PublishAsync(IngestMessage message, CancellationToken cancellationToken = default) { await channel.Writer.WriteAsync( @@ -29,6 +32,9 @@ await channel.Writer.WriteAsync( Interlocked.Increment(ref depth); } + /// + /// Consume asynchronously. + /// public async IAsyncEnumerable ConsumeAsync( [EnumeratorCancellation] CancellationToken cancellationToken = default) { @@ -39,12 +45,21 @@ public async IAsyncEnumerable ConsumeAsync( } } + /// + /// Ack asynchronously. + /// public Task AckAsync(string messageId, CancellationToken cancellationToken = default) => Task.CompletedTask; // The channel drops anything that is not pulled; an unacked envelope is never redelivered. + /// + /// Gets the redelivers unacknowledged. + /// public bool RedeliversUnacknowledged => false; + /// + /// Gets the queue depth asynchronously. + /// public Task GetQueueDepthAsync(CancellationToken cancellationToken = default) => Task.FromResult(Math.Max(0L, Interlocked.Read(ref depth))); } diff --git a/Proxytrace.Messaging/Internal/RedisIngestionStream.cs b/Proxytrace.Messaging/Internal/RedisIngestionStream.cs index 3ab1f1890..63b6a9810 100644 --- a/Proxytrace.Messaging/Internal/RedisIngestionStream.cs +++ b/Proxytrace.Messaging/Internal/RedisIngestionStream.cs @@ -26,6 +26,9 @@ internal sealed class RedisIngestionStream : IIngestionStream private readonly MessagingConfiguration configuration; private readonly ILogger logger; + /// + /// Initializes a new instance of the class. + /// public RedisIngestionStream( IConnectionMultiplexer connection, MessagingConfiguration configuration, @@ -38,6 +41,9 @@ public RedisIngestionStream( private IDatabase Database => connection.GetDatabase(); + /// + /// Publishes asynchronously. + /// public async Task PublishAsync(IngestMessage message, CancellationToken cancellationToken = default) { cancellationToken.ThrowIfCancellationRequested(); @@ -70,6 +76,9 @@ await Database.StreamAddAsync( flags: CommandFlags.None); } + /// + /// Consume asynchronously. + /// public async IAsyncEnumerable ConsumeAsync( [EnumeratorCancellation] CancellationToken cancellationToken = default) { @@ -119,6 +128,9 @@ public async IAsyncEnumerable ConsumeAsync( } } + /// + /// Ack asynchronously. + /// public async Task AckAsync(string messageId, CancellationToken cancellationToken = default) => await Database.StreamAcknowledgeAsync( configuration.Stream, @@ -127,8 +139,14 @@ public async Task AckAsync(string messageId, CancellationToken cancellationToken flags: CommandFlags.None); // Pending entries are reclaimed via XAUTOCLAIM and redelivered, so an unacked envelope reappears. + /// + /// Gets the redelivers unacknowledged. + /// public bool RedeliversUnacknowledged => true; + /// + /// Gets the queue depth asynchronously. + /// public async Task GetQueueDepthAsync(CancellationToken cancellationToken = default) { // Depth is observability-only and runs on the hot dashboard path. The multiplexer is diff --git a/Proxytrace.Messaging/MessagingConfiguration.cs b/Proxytrace.Messaging/MessagingConfiguration.cs index 14a302712..248174657 100644 --- a/Proxytrace.Messaging/MessagingConfiguration.cs +++ b/Proxytrace.Messaging/MessagingConfiguration.cs @@ -1,5 +1,8 @@ namespace Proxytrace.Messaging; +/// +/// Specifies the messaging provider. +/// public enum MessagingProvider { /// In-memory channel. Single process only — used by the test suite and local runs. @@ -9,14 +12,29 @@ public enum MessagingProvider Redis, } +/// +/// Configuration for messaging. +/// public sealed record MessagingConfiguration { + /// + /// Gets or sets the provider. + /// public MessagingProvider Provider { get; init; } = MessagingProvider.InProcess; + /// + /// Gets or sets the redis connection string. + /// public string RedisConnectionString { get; init; } = "localhost:6379"; + /// + /// Gets or sets the stream. + /// public string Stream { get; init; } = "proxytrace:ingest"; + /// + /// Gets or sets the consumer group. + /// public string ConsumerGroup { get; init; } = "proxytrace-app"; /// Per-instance consumer name within the group; defaults to the machine name. diff --git a/Proxytrace.Messaging/Module.cs b/Proxytrace.Messaging/Module.cs index 612399f9e..ef790ffa8 100644 --- a/Proxytrace.Messaging/Module.cs +++ b/Proxytrace.Messaging/Module.cs @@ -13,6 +13,9 @@ public sealed class Module : Autofac.Module { private readonly MessagingConfiguration configuration; + /// + /// Initializes a new instance of the class. + /// public Module(MessagingConfiguration? configuration = null) => this.configuration = configuration ?? new MessagingConfiguration(); diff --git a/Proxytrace.Proxy.Api/Internal/ProxyStoredLicenseService.cs b/Proxytrace.Proxy.Api/Internal/ProxyStoredLicenseService.cs index 665754303..74e66fb7f 100644 --- a/Proxytrace.Proxy.Api/Internal/ProxyStoredLicenseService.cs +++ b/Proxytrace.Proxy.Api/Internal/ProxyStoredLicenseService.cs @@ -24,6 +24,9 @@ internal sealed class ProxyStoredLicenseService : BackgroundService private string? appliedJwt; + /// + /// Initializes a new instance of the class. + /// public ProxyStoredLicenseService( ILifetimeScope rootScope, ILicenseActivator activator, diff --git a/Proxytrace.Proxy.Api/Internal/UnusedAgentNameGenerator.cs b/Proxytrace.Proxy.Api/Internal/UnusedAgentNameGenerator.cs index b734bf943..5cfa063d2 100644 --- a/Proxytrace.Proxy.Api/Internal/UnusedAgentNameGenerator.cs +++ b/Proxytrace.Proxy.Api/Internal/UnusedAgentNameGenerator.cs @@ -13,6 +13,9 @@ namespace Proxytrace.Proxy.Api.Internal; /// internal sealed class UnusedAgentNameGenerator : IAgentNameGenerator { + /// + /// Generates the name asynchronously. + /// public Task GenerateNameAsync( IPromptTemplate systemPrompt, IProject project, diff --git a/Proxytrace.Proxy.Api/Internal/UnusedProviderClient.cs b/Proxytrace.Proxy.Api/Internal/UnusedProviderClient.cs index 75348ae79..4fa1dc693 100644 --- a/Proxytrace.Proxy.Api/Internal/UnusedProviderClient.cs +++ b/Proxytrace.Proxy.Api/Internal/UnusedProviderClient.cs @@ -11,9 +11,15 @@ namespace Proxytrace.Proxy.Api.Internal; /// internal sealed class UnusedProviderClient : IProviderClient { + /// + /// Verifies the connection asynchronously. + /// public Task VerifyConnectionAsync(CancellationToken cancellationToken = default) => throw new NotSupportedException("Provider client operations are not available in the ingestion proxy."); + /// + /// Gets the models asynchronously. + /// public Task> GetModelsAsync(CancellationToken cancellationToken = default) => throw new NotSupportedException("Provider client operations are not available in the ingestion proxy."); } diff --git a/Proxytrace.Proxy/Controllers/OpenAiProxyController.cs b/Proxytrace.Proxy/Controllers/OpenAiProxyController.cs index fe082a36b..9f06df1a8 100644 --- a/Proxytrace.Proxy/Controllers/OpenAiProxyController.cs +++ b/Proxytrace.Proxy/Controllers/OpenAiProxyController.cs @@ -38,6 +38,9 @@ public class OpenAiProxyController : ControllerBase // KestrelServerLimits.MaxRequestBodySize to the *same* number: Kestrel's 30 MB default rejects // first otherwise, and this cap — plus the 413 it produces — is unreachable dead code. // See Proxytrace.Proxy.Api/Program.cs. + /// + /// The max request body bytes constant value. + /// public const long MaxRequestBodyBytes = 64L * 1024 * 1024; private const int MaxCapturedResponseChars = 16 * 1024 * 1024; @@ -116,6 +119,9 @@ public class OpenAiProxyController : ControllerBase private readonly KioskEndpointOptions kioskEndpoint; private readonly ILogger logger; + /// + /// Initializes a new instance of the class. + /// public OpenAiProxyController( IHttpClientFactory httpClientFactory, IIngestionStream stream, @@ -140,6 +146,9 @@ public OpenAiProxyController( // upstream-provider-key auth path) and the legacy `/openai/v1/…` form (project derived from a // Proxytrace-issued key). The literal `openai/v1/…` template is matched ahead of the // parameterised one, so `project` is only bound for the scoped form. + /// + /// Proxy. + /// [Route("openai/v1/{**path}")] [Route("{project}/openai/v1/{**path}")] [HttpGet, HttpPost, HttpPut, HttpDelete, HttpPatch, HttpHead, HttpOptions] @@ -268,6 +277,9 @@ await RejectBlockedRequestAsync( // calls are forwarded to the provider's host ORIGIN and are deliberately NOT ingested/traced. // The literal `{project}/openai/v1/…` route out-ranks this all-parameter catch-all, so LLM calls // never reach here. + /// + /// Passthrough. + /// [Route("{project}/{**rest}")] [HttpGet, HttpPost, HttpPut, HttpDelete, HttpPatch, HttpHead, HttpOptions] public async Task Passthrough(string project, string? rest, CancellationToken cancellationToken) diff --git a/Proxytrace.Proxy/Internal/ApiKeyResolver.cs b/Proxytrace.Proxy/Internal/ApiKeyResolver.cs index e86807fc6..daf5e7a1c 100644 --- a/Proxytrace.Proxy/Internal/ApiKeyResolver.cs +++ b/Proxytrace.Proxy/Internal/ApiKeyResolver.cs @@ -11,6 +11,9 @@ internal sealed class ApiKeyResolver : IApiKeyResolver private readonly IModelProviderRepository providers; private readonly IProjectRepository projects; + /// + /// Initializes a new instance of the class. + /// public ApiKeyResolver( IApiKeyRepository apiKeys, IModelProviderRepository providers, @@ -28,6 +31,9 @@ public ApiKeyResolver( // effect on the next request; when the database is unreachable the proxy fails closed instead // of serving stale credentials. The per-request cost is a few indexed point lookups (guarded by // the proxyResolve* perf budgets), negligible next to an upstream LLM round trip. + /// + /// Resolves asynchronously. + /// public async Task ResolveAsync(string rawKey, string? projectSlug, CancellationToken cancellationToken) { // Proxytrace-issued key path wins on collisions. The key already carries its project, so the diff --git a/Proxytrace.Proxy/Internal/BudgetBlocker.cs b/Proxytrace.Proxy/Internal/BudgetBlocker.cs index e296a62bd..0fc1c9cc5 100644 --- a/Proxytrace.Proxy/Internal/BudgetBlocker.cs +++ b/Proxytrace.Proxy/Internal/BudgetBlocker.cs @@ -20,11 +20,17 @@ internal sealed class BudgetBlocker : IBudgetBlocker { private readonly IBudgetBlockProvider blockProvider; + /// + /// Initializes a new instance of the class. + /// public BudgetBlocker(IBudgetBlockProvider blockProvider) { this.blockProvider = blockProvider; } + /// + /// Evaluates asynchronously. + /// public async Task EvaluateAsync( Guid projectId, string? agentName, diff --git a/Proxytrace.Proxy/Internal/CachedBlockingRuleProvider.cs b/Proxytrace.Proxy/Internal/CachedBlockingRuleProvider.cs index 3f428c259..d8f63e370 100644 --- a/Proxytrace.Proxy/Internal/CachedBlockingRuleProvider.cs +++ b/Proxytrace.Proxy/Internal/CachedBlockingRuleProvider.cs @@ -23,6 +23,9 @@ internal sealed class CachedBlockingRuleProvider : IBlockingRuleProvider private readonly TimeSpan ttl; private readonly ILogger logger; + /// + /// Initializes a new instance of the class. + /// public CachedBlockingRuleProvider( ICustomAnomalyDetectorRepository detectors, ILicenseService license, @@ -37,6 +40,9 @@ public CachedBlockingRuleProvider( this.logger = logger; } + /// + /// Gets the rules asynchronously. + /// public async Task> GetRulesAsync( Guid projectId, CancellationToken cancellationToken) diff --git a/Proxytrace.Proxy/Internal/CachedBudgetBlockProvider.cs b/Proxytrace.Proxy/Internal/CachedBudgetBlockProvider.cs index 914f7278a..7b0c3258f 100644 --- a/Proxytrace.Proxy/Internal/CachedBudgetBlockProvider.cs +++ b/Proxytrace.Proxy/Internal/CachedBudgetBlockProvider.cs @@ -33,6 +33,9 @@ internal sealed class CachedBudgetBlockProvider : IBudgetBlockProvider private readonly TimeSpan ttl; private readonly ILogger logger; + /// + /// Initializes a new instance of the class. + /// public CachedBudgetBlockProvider( ICostLimitBreachRepository breaches, ILicenseService license, @@ -49,6 +52,9 @@ public CachedBudgetBlockProvider( this.logger = logger; } + /// + /// Gets the blocks asynchronously. + /// public async Task> GetBlocksAsync( Guid projectId, CancellationToken cancellationToken) diff --git a/Proxytrace.Proxy/Internal/RequestBlocker.cs b/Proxytrace.Proxy/Internal/RequestBlocker.cs index 2476c96ce..db0abd370 100644 --- a/Proxytrace.Proxy/Internal/RequestBlocker.cs +++ b/Proxytrace.Proxy/Internal/RequestBlocker.cs @@ -16,11 +16,17 @@ internal sealed class RequestBlocker : IRequestBlocker { private readonly IBlockingRuleProvider ruleProvider; + /// + /// Initializes a new instance of the class. + /// public RequestBlocker(IBlockingRuleProvider ruleProvider) { this.ruleProvider = ruleProvider; } + /// + /// Evaluates asynchronously. + /// public async Task EvaluateAsync( Guid projectId, string? agentName, diff --git a/Proxytrace.Storage/Internal/AgentCallPreviewBackfillService.cs b/Proxytrace.Storage/Internal/AgentCallPreviewBackfillService.cs index a5508437e..4234db2d7 100644 --- a/Proxytrace.Storage/Internal/AgentCallPreviewBackfillService.cs +++ b/Proxytrace.Storage/Internal/AgentCallPreviewBackfillService.cs @@ -38,6 +38,9 @@ internal sealed class AgentCallPreviewBackfillService : IHostedService // batchSize/retryDelay default to production values and are constructor-injectable purely so tests // can exercise the multi-batch loop and the retry/never-throw wrapper without large data or waits. // Autofac supplies only contextFactory/logger and uses these defaults for the optional parameters. + /// + /// Initializes a new instance of the class. + /// public AgentCallPreviewBackfillService( Func> contextFactory, ILogger logger, @@ -50,6 +53,9 @@ public AgentCallPreviewBackfillService( this.retryDelay = retryDelay ?? TimeSpan.FromSeconds(2); } + /// + /// Starts asynchronously. + /// public async Task StartAsync(CancellationToken cancellationToken) { for (var attempt = 1; attempt <= MaxAttempts; attempt++) @@ -125,5 +131,8 @@ public async Task BackfillAsync(CancellationToken cancellationToken) return total; } + /// + /// Stops asynchronously. + /// public Task StopAsync(CancellationToken cancellationToken) => Task.CompletedTask; } diff --git a/Proxytrace.Storage/Internal/AgentCallToolBackfillService.cs b/Proxytrace.Storage/Internal/AgentCallToolBackfillService.cs index 87b85b93f..be4c082a0 100644 --- a/Proxytrace.Storage/Internal/AgentCallToolBackfillService.cs +++ b/Proxytrace.Storage/Internal/AgentCallToolBackfillService.cs @@ -43,6 +43,9 @@ internal sealed class AgentCallToolBackfillService : IHostedService // batchSize/retryDelay default to production values and are constructor-injectable purely so tests // can exercise the multi-batch loop and the retry/never-throw wrapper without large data or waits. // Autofac supplies only contextFactory/logger and uses these defaults for the optional parameters. + /// + /// Initializes a new instance of the class. + /// public AgentCallToolBackfillService( Func> contextFactory, ILogger logger, @@ -55,6 +58,9 @@ public AgentCallToolBackfillService( this.retryDelay = retryDelay ?? TimeSpan.FromSeconds(2); } + /// + /// Starts asynchronously. + /// public async Task StartAsync(CancellationToken cancellationToken) { for (var attempt = 1; attempt <= MaxAttempts; attempt++) @@ -149,5 +155,8 @@ orderby call.CreatedAt return total; } + /// + /// Stops asynchronously. + /// public Task StopAsync(CancellationToken cancellationToken) => Task.CompletedTask; } diff --git a/Proxytrace.Storage/Internal/DatabaseInitializationService.cs b/Proxytrace.Storage/Internal/DatabaseInitializationService.cs index 561ef6c44..8c396de78 100644 --- a/Proxytrace.Storage/Internal/DatabaseInitializationService.cs +++ b/Proxytrace.Storage/Internal/DatabaseInitializationService.cs @@ -15,6 +15,9 @@ internal class DatabaseInitializationService : IHostedService, IDatabaseInitiali private readonly StorageConfiguration configuration; private readonly ILogger logger; + /// + /// Initializes a new instance of the class. + /// public DatabaseInitializationService( IServiceProvider serviceProvider, StorageConfiguration configuration, diff --git a/Proxytrace.Storage/Internal/Entities/Agent/AgentConfig.cs b/Proxytrace.Storage/Internal/Entities/Agent/AgentConfig.cs index d0dc607db..abc1bfe9d 100644 --- a/Proxytrace.Storage/Internal/Entities/Agent/AgentConfig.cs +++ b/Proxytrace.Storage/Internal/Entities/Agent/AgentConfig.cs @@ -23,6 +23,9 @@ internal class AgentConfig : AbstractEntityConfiguration, IMapper endpoints; private readonly Lazy> versions; + /// + /// Initializes a new instance of the class. + /// public AgentConfig( IAgent.CreateExisting factory, IModelParameters.Create modelParametersFactory, @@ -39,6 +42,9 @@ public AgentConfig( this.versions = versions; } + /// + /// Configures the application request pipeline. + /// public override void Configure(EntityTypeBuilder builder) { builder.HasIndex(e => e.IsSystemAgent); @@ -67,6 +73,9 @@ public override void Configure(EntityTypeBuilder builder) ); } + /// + /// Maps. + /// public async Task Map(AgentEntity stored, CancellationToken cancellationToken = default) { var project = await projects.GetAsync(stored.Project, cancellationToken); @@ -85,6 +94,9 @@ public async Task Map(AgentEntity stored, CancellationToken cancellation existing: stored); } + /// + /// Maps. + /// public Task Map(IAgent domain, CancellationToken cancellationToken = default) => new AgentEntity { diff --git a/Proxytrace.Storage/Internal/Entities/Agent/AgentEntity.cs b/Proxytrace.Storage/Internal/Entities/Agent/AgentEntity.cs index ba3cfae34..30b1bf5c1 100644 --- a/Proxytrace.Storage/Internal/Entities/Agent/AgentEntity.cs +++ b/Proxytrace.Storage/Internal/Entities/Agent/AgentEntity.cs @@ -9,10 +9,25 @@ internal record SystemPromptData(string Name, string Template); [Cacheable] internal record AgentEntity : Entity, IArchivableEntity { + /// + /// Gets or sets the name. + /// public required string Name { get; init; } + /// + /// Gets or sets the project. + /// public required Guid Project { get; init; } + /// + /// Gets or sets the endpoint. + /// public required Guid Endpoint { get; init; } + /// + /// Gets or sets the is system agent. + /// public required bool IsSystemAgent { get; init; } + /// + /// Gets or sets the model parameters. + /// public required ModelParametersData ModelParameters { get; init; } /// diff --git a/Proxytrace.Storage/Internal/Entities/Agent/AgentRepository.cs b/Proxytrace.Storage/Internal/Entities/Agent/AgentRepository.cs index c1ed6c01f..1b3d4cab4 100644 --- a/Proxytrace.Storage/Internal/Entities/Agent/AgentRepository.cs +++ b/Proxytrace.Storage/Internal/Entities/Agent/AgentRepository.cs @@ -30,6 +30,9 @@ internal class AgentRepository : ArchivableRepository, IAge private readonly IAgentVersionFingerprinter fingerprinter; private readonly IEntityCache? versionCache; + /// + /// Initializes a new instance of the class. + /// public AgentRepository( IMapper mapper, Func contextFactory, @@ -58,6 +61,9 @@ public AgentRepository( this.versionCache = versionCache; } + /// + /// Upsert asynchronously. + /// public override async Task UpsertAsync(IAgent entity, CancellationToken cancellationToken = default) { if (await this.ContainsAsync(entity.Id, cancellationToken)) @@ -67,6 +73,9 @@ public override async Task UpsertAsync(IAgent entity, CancellationToken return await PersistWithInitialVersionAsync(entity, cancellationToken); } + /// + /// Gets the or create asynchronously. + /// public async Task GetOrCreateAsync( IPromptTemplate systemPrompt, IReadOnlyList tools, @@ -125,6 +134,9 @@ public override async Task AddAsync(IAgent entity, CancellationToken can return await PersistWithInitialVersionAsync(entity, cancellationToken); } + /// + /// Creates the with initial version asynchronously. + /// public Task CreateWithInitialVersionAsync( string name, IPromptTemplate systemPrompt, @@ -202,15 +214,27 @@ private Task SetCurrentVersionIdAsync(Guid agentId, Guid versionId, Cancellation InvalidateCacheEntry(agentId); }); + /// + /// Gets the agent fingerprint. + /// public string GetAgentFingerprint(IPromptTemplate systemPrompt, IReadOnlyCollection tools) => fingerprinter.Strict(systemPrompt, tools); + /// + /// Gets the agent fingerprint. + /// public string GetAgentFingerprint(IAgent agent) => GetAgentFingerprint(agent.SystemPrompt, agent.Tools); + /// + /// Sets the current version asynchronously. + /// public Task SetCurrentVersionAsync(Guid agentId, Guid versionId, CancellationToken cancellationToken = default) => SetCurrentVersionIdAsync(agentId, versionId, cancellationToken); + /// + /// Counts the non system asynchronously. + /// public async Task CountNonSystemAsync(CancellationToken cancellationToken = default) => await contextFactory() .Set() @@ -218,6 +242,9 @@ public async Task CountNonSystemAsync(CancellationToken cancellationToken = // Archived agents are soft-deleted — they must not consume a licensed agent slot. .CountAsync(e => !e.IsSystemAgent && !e.IsArchived, cancellationToken); + /// + /// Finds the by name asynchronously. + /// public async Task FindByNameAsync(IProject project, string name, CancellationToken cancellationToken = default) { ArgumentNullException.ThrowIfNull(project); @@ -232,6 +259,9 @@ public async Task CountNonSystemAsync(CancellationToken cancellationToken = return id is { } agentId ? await this.GetAsync(agentId, cancellationToken) : null; } + /// + /// Gets the by project asynchronously. + /// public async Task> GetByProjectAsync(Guid projectId, CancellationToken cancellationToken = default) { var stored = await contextFactory() @@ -244,6 +274,9 @@ public async Task> GetByProjectAsync(Guid projectId, Cance return await Map(stored, cancellationToken); } + /// + /// Gets the project id asynchronously. + /// public async Task GetProjectIdAsync(Guid agentId, CancellationToken cancellationToken = default) => await contextFactory() .Set() diff --git a/Proxytrace.Storage/Internal/Entities/AgentCall/AgentCallConfig.cs b/Proxytrace.Storage/Internal/Entities/AgentCall/AgentCallConfig.cs index 80e85f11f..8e5b2a27b 100644 --- a/Proxytrace.Storage/Internal/Entities/AgentCall/AgentCallConfig.cs +++ b/Proxytrace.Storage/Internal/Entities/AgentCall/AgentCallConfig.cs @@ -27,6 +27,9 @@ internal class AgentCallConfig : AbstractEntityConfiguration, I private readonly ICompletion.Create completionFactory; private readonly IRepository endpoints; + /// + /// Initializes a new instance of the class. + /// public AgentCallConfig( IAgentCall.CreateExisting factory, IModelParameters.Create modelParametersFactory, @@ -45,6 +48,9 @@ public AgentCallConfig( this.endpoints = endpoints; } + /// + /// Configures the application request pipeline. + /// public override void Configure(EntityTypeBuilder builder) { // Composite (AgentVersionId, CreatedAt): serves the agent/project-scoped trace list and @@ -118,6 +124,9 @@ public override void Configure(EntityTypeBuilder builder) .OnDelete(DeleteBehavior.Restrict); } + /// + /// Maps. + /// public async Task Map(AgentCallEntity stored, CancellationToken cancellationToken = default) { IAgentVersion version = await versions.GetAsync(stored.AgentVersionId, cancellationToken); @@ -148,6 +157,9 @@ stored.Response is not null apiKeyId: stored.ApiKeyId); } + /// + /// Maps. + /// public Task Map(IAgentCall domain, CancellationToken cancellationToken = default) => new AgentCallEntity { diff --git a/Proxytrace.Storage/Internal/Entities/AgentCall/AgentCallEntity.cs b/Proxytrace.Storage/Internal/Entities/AgentCall/AgentCallEntity.cs index df5bffbb9..77ebe010f 100644 --- a/Proxytrace.Storage/Internal/Entities/AgentCall/AgentCallEntity.cs +++ b/Proxytrace.Storage/Internal/Entities/AgentCall/AgentCallEntity.cs @@ -7,42 +7,105 @@ namespace Proxytrace.Storage.Internal.Entities.AgentCall; [StoredDomainEntity(typeof(IAgentCall))] internal record AgentCallEntity : Entity { + /// + /// Gets or sets the agent version id. + /// public required Guid AgentVersionId { get; init; } + /// + /// Gets or sets the endpoint id. + /// public required Guid EndpointId { get; init; } + /// + /// Gets or sets the request. + /// public required Conversation Request { get; init; } + /// + /// Gets or sets the response. + /// public required AssistantMessage? Response { get; init; } + /// + /// Gets or sets the input tokens. + /// public required ulong? InputTokens { get; init; } + /// + /// Gets or sets the output tokens. + /// public required ulong? OutputTokens { get; init; } + /// + /// Gets or sets the cached input tokens. + /// public required ulong? CachedInputTokens { get; init; } + /// + /// Gets or sets the latency ms. + /// public required double? LatencyMs { get; init; } + /// + /// Gets or sets the http status. + /// public required int HttpStatus { get; init; } + /// + /// Gets or sets the finish reason. + /// public required string? FinishReason { get; init; } + /// + /// Gets or sets the error message. + /// public required string? ErrorMessage { get; init; } + /// + /// Gets or sets the model parameters. + /// public required ModelParametersData ModelParameters { get; init; } + /// + /// Gets or sets the conversation id. + /// public required Guid? ConversationId { get; init; } + /// + /// Gets or sets the session id. + /// public required Guid? SessionId { get; init; } // The inbound Proxytrace API key this call authenticated with, or null when unattributable (the // upstream-key auth path, or any row written before key attribution existed). Deliberately // FK-free — like SessionId/ConversationId — so revoking a key never cascades away telemetry. // Backs the per-key cost breakdown and key-scoped budgets. + /// + /// Gets or sets the api key id. + /// public Guid? ApiKeyId { get; init; } // Outlier characteristics flagged at ingestion (bitmask). 0 = not an outlier. Persisted as a // single byte; a partial index (see AgentCallConfig) serves the "outliers only" trace filter. + /// + /// Gets or sets the outlier flags. + /// public OutlierFlags OutlierFlags { get; init; } // Denormalised summaries populated at write time so the traces-list query can project scalar // columns only, without reading/deserialising the Request and Response payload columns. + /// + /// Gets or sets the request preview. + /// public string? RequestPreview { get; init; } + /// + /// Gets or sets the response tool request count. + /// public int ResponseToolRequestCount { get; init; } // Denormalised at write time so token/cache sorts and range filters hit plain indexed columns // instead of per-row expressions. Null when the call reported no usage. + /// + /// Gets or sets the total tokens. + /// public ulong? TotalTokens { get; init; } + /// + /// Gets or sets the cache hit rate. + /// public double? CacheHitRate { get; init; } // One row per distinct tool name requested in the response — backs the ToolName filter's EXISTS // semi-join and the project-scoped tool-name picker. See AgentCallToolEntity/AgentCallToolConfig. + /// + /// Gets or sets the tools. + /// public List Tools { get; init; } = []; } diff --git a/Proxytrace.Storage/Internal/Entities/AgentCall/AgentCallPreview.cs b/Proxytrace.Storage/Internal/Entities/AgentCall/AgentCallPreview.cs index 108118f44..cf50cc2c6 100644 --- a/Proxytrace.Storage/Internal/Entities/AgentCall/AgentCallPreview.cs +++ b/Proxytrace.Storage/Internal/Entities/AgentCall/AgentCallPreview.cs @@ -11,6 +11,9 @@ namespace Proxytrace.Storage.Internal.Entities.AgentCall; /// internal static partial class AgentCallPreview { + /// + /// The max length constant value. + /// public const int MaxLength = 1000; /// First user message in the request, whitespace-collapsed and truncated to diff --git a/Proxytrace.Storage/Internal/Entities/AgentCall/AgentCallRepository.cs b/Proxytrace.Storage/Internal/Entities/AgentCall/AgentCallRepository.cs index 89d6db324..271dd14a4 100644 --- a/Proxytrace.Storage/Internal/Entities/AgentCall/AgentCallRepository.cs +++ b/Proxytrace.Storage/Internal/Entities/AgentCall/AgentCallRepository.cs @@ -29,6 +29,9 @@ internal class AgentCallRepository : AbstractRepository agents; private readonly IRepository endpoints; + /// + /// Initializes a new instance of the class. + /// public AgentCallRepository( IMapper mapper, Func contextFactory, @@ -46,6 +49,9 @@ public AgentCallRepository( this.endpoints = endpoints; } + /// + /// Gets the filtered asynchronously. + /// public async Task<(IReadOnlyList Items, int Total)> GetFilteredAsync( AgentCallFilter filter, int page, @@ -70,6 +76,9 @@ public AgentCallRepository( return (items, total); } + /// + /// Gets the filtered list asynchronously. + /// public async Task<(IReadOnlyList Items, int Total)> GetFilteredListAsync( AgentCallFilter filter, int page, @@ -183,6 +192,9 @@ private sealed record ListRow( Guid? SessionId, OutlierFlags OutlierFlags); + /// + /// Gets the histogram asynchronously. + /// public async Task> GetHistogramAsync( AgentCallFilter filter, int buckets, @@ -248,6 +260,9 @@ public async Task> GetHistogramAsync( aggregated.Select(a => (a.Index, a.Total, a.Errors)), from, to, buckets); } + /// + /// Gets the summary asynchronously. + /// public async Task GetSummaryAsync( AgentCallFilter filter, CancellationToken cancellationToken = default) @@ -518,6 +533,9 @@ private static IOrderedQueryable OrderNullable( ? query.OrderBy(isNull).ThenByDescending(key).ThenByDescending(e => e.Id) : query.OrderBy(isNull).ThenBy(key).ThenBy(e => e.Id); + /// + /// Gets the last call times asynchronously. + /// public async Task> GetLastCallTimesAsync( CancellationToken cancellationToken = default) { @@ -534,6 +552,9 @@ into g return result; } + /// + /// Gets the last call time asynchronously. + /// public async Task GetLastCallTimeAsync( Guid agentId, CancellationToken cancellationToken = default) @@ -556,6 +577,9 @@ into g .MaxAsync(cancellationToken); } + /// + /// Finds the latest by conversation id asynchronously. + /// public async Task FindLatestByConversationIdAsync( Guid conversationId, IProject project, @@ -578,6 +602,9 @@ into g : await mapper.Map(stored, cancellationToken); } + /// + /// Removes the older than asynchronously. + /// public async Task RemoveOlderThanAsync(DateTimeOffset cutoffDate, CancellationToken cancellationToken = default) { var context = contextFactory(); @@ -601,6 +628,9 @@ public async Task RemoveOlderThanAsync(DateTimeOffset cutoffDate, Cancellat return toRemove.Count; } + /// + /// Gets the session removals older than asynchronously. + /// public async Task> GetSessionRemovalsOlderThanAsync( DateTimeOffset cutoffDate, CancellationToken cancellationToken = default) @@ -634,6 +664,9 @@ public async Task> GetSessionRemovalsOlderTha return removals; } + /// + /// Sets the outlier flag asynchronously. + /// public async Task SetOutlierFlagAsync(Guid id, OutlierFlags flag, CancellationToken cancellationToken = default) { var context = contextFactory(); @@ -659,6 +692,9 @@ await query.ExecuteUpdateAsync( await context.SaveChangesAsync(cancellationToken); } + /// + /// Gets the tool names asynchronously. + /// public async Task> GetToolNamesAsync( Guid projectId, Guid? agentId = null, CancellationToken cancellationToken = default) { diff --git a/Proxytrace.Storage/Internal/Entities/AgentCall/AgentCallToolConfig.cs b/Proxytrace.Storage/Internal/Entities/AgentCall/AgentCallToolConfig.cs index f5d049c40..625a6b2f4 100644 --- a/Proxytrace.Storage/Internal/Entities/AgentCall/AgentCallToolConfig.cs +++ b/Proxytrace.Storage/Internal/Entities/AgentCall/AgentCallToolConfig.cs @@ -5,6 +5,9 @@ namespace Proxytrace.Storage.Internal.Entities.AgentCall; internal class AgentCallToolConfig : AbstractEntityConfiguration { + /// + /// Configures the application request pipeline. + /// public override void Configure(EntityTypeBuilder builder) { builder.Property(t => t.ToolName).HasMaxLength(256); diff --git a/Proxytrace.Storage/Internal/Entities/AgentCall/AgentCallToolEntity.cs b/Proxytrace.Storage/Internal/Entities/AgentCall/AgentCallToolEntity.cs index a788a80cd..5dedd323b 100644 --- a/Proxytrace.Storage/Internal/Entities/AgentCall/AgentCallToolEntity.cs +++ b/Proxytrace.Storage/Internal/Entities/AgentCall/AgentCallToolEntity.cs @@ -6,8 +6,20 @@ namespace Proxytrace.Storage.Internal.Entities.AgentCall; // data — project-wide via (ProjectId, ToolName), agent-scoped via (ProjectId, AgentId, ToolName). internal record AgentCallToolEntity : Entity { + /// + /// Gets or sets the agent call id. + /// public required Guid AgentCallId { get; init; } + /// + /// Gets or sets the project id. + /// public required Guid ProjectId { get; init; } + /// + /// Gets or sets the agent id. + /// public required Guid AgentId { get; init; } + /// + /// Gets or sets the tool name. + /// public required string ToolName { get; init; } } diff --git a/Proxytrace.Storage/Internal/Entities/AgentVersion/AgentVersionConfig.cs b/Proxytrace.Storage/Internal/Entities/AgentVersion/AgentVersionConfig.cs index a4598e2c7..e66f53939 100644 --- a/Proxytrace.Storage/Internal/Entities/AgentVersion/AgentVersionConfig.cs +++ b/Proxytrace.Storage/Internal/Entities/AgentVersion/AgentVersionConfig.cs @@ -17,6 +17,9 @@ internal class AgentVersionConfig : AbstractEntityConfiguration + /// Initializes a new instance of the class. + /// public AgentVersionConfig( IAgentVersion.CreateExisting factory, IPromptTemplate.Create promptTemplateFactory, @@ -29,6 +32,9 @@ public AgentVersionConfig( this.fingerprinter = fingerprinter; } + /// + /// Configures the application request pipeline. + /// public override void Configure(EntityTypeBuilder builder) { builder.HasIndex(e => new { e.AgentId, e.VersionNumber }).IsUnique(); @@ -63,6 +69,9 @@ public override void Configure(EntityTypeBuilder builder) v => serializer.Deserialize>(v) ?? Array.Empty()); } + /// + /// Maps. + /// public Task Map(AgentVersionEntity stored, CancellationToken cancellationToken = default) { var prompt = promptTemplateFactory(stored.SystemPrompt.Name, stored.SystemPrompt.Template); @@ -75,6 +84,9 @@ public Task Map(AgentVersionEntity stored, CancellationToken canc existing: stored)); } + /// + /// Maps. + /// public Task Map(IAgentVersion domain, CancellationToken cancellationToken = default) => Task.FromResult(new AgentVersionEntity { diff --git a/Proxytrace.Storage/Internal/Entities/AgentVersion/AgentVersionEntity.cs b/Proxytrace.Storage/Internal/Entities/AgentVersion/AgentVersionEntity.cs index 01094970a..6bd40f811 100644 --- a/Proxytrace.Storage/Internal/Entities/AgentVersion/AgentVersionEntity.cs +++ b/Proxytrace.Storage/Internal/Entities/AgentVersion/AgentVersionEntity.cs @@ -8,6 +8,9 @@ namespace Proxytrace.Storage.Internal.Entities.AgentVersion; [Cacheable] internal record AgentVersionEntity : Entity { + /// + /// Gets or sets the agent id. + /// public required Guid AgentId { get; init; } /// @@ -15,10 +18,19 @@ internal record AgentVersionEntity : Entity /// public required Guid Project { get; init; } + /// + /// Gets or sets the version number. + /// public required int VersionNumber { get; init; } + /// + /// Gets or sets the system prompt. + /// public required SystemPromptData SystemPrompt { get; init; } + /// + /// Gets or sets the tools. + /// public required IReadOnlyList Tools { get; init; } /// diff --git a/Proxytrace.Storage/Internal/Entities/AgentVersion/AgentVersionRepository.cs b/Proxytrace.Storage/Internal/Entities/AgentVersion/AgentVersionRepository.cs index d9f2e854b..89ddbaddc 100644 --- a/Proxytrace.Storage/Internal/Entities/AgentVersion/AgentVersionRepository.cs +++ b/Proxytrace.Storage/Internal/Entities/AgentVersion/AgentVersionRepository.cs @@ -16,6 +16,9 @@ internal class AgentVersionRepository : AbstractRepository + /// Initializes a new instance of the class. + /// public AgentVersionRepository( IMapper mapper, Func contextFactory, @@ -28,6 +31,9 @@ public AgentVersionRepository( this.fingerprinter = fingerprinter; } + /// + /// Finds the by strict fingerprint asynchronously. + /// public async Task FindByStrictFingerprintAsync( IProject project, IPromptTemplate systemPrompt, @@ -42,6 +48,9 @@ public AgentVersionRepository( return existing is null ? null : await mapper.Map(existing, cancellationToken); } + /// + /// Gets the by loose fingerprint asynchronously. + /// public async Task> GetByLooseFingerprintAsync( IProject project, IPromptTemplate systemPrompt, @@ -57,6 +66,9 @@ public async Task> GetByLooseFingerprintAsync( return await Map(stored, cancellationToken); } + /// + /// Gets the by agent asynchronously. + /// public async Task> GetByAgentAsync( IAgent agent, CancellationToken cancellationToken = default) @@ -70,6 +82,9 @@ public async Task> GetByAgentAsync( return await Map(stored, cancellationToken); } + /// + /// Gets the strict fingerprint. + /// public string GetStrictFingerprint(IPromptTemplate systemPrompt, IReadOnlyCollection tools) => fingerprinter.Strict(systemPrompt, tools); } diff --git a/Proxytrace.Storage/Internal/Entities/ApiKey/ApiKeyConfig.cs b/Proxytrace.Storage/Internal/Entities/ApiKey/ApiKeyConfig.cs index c17fa9a32..7418481ca 100644 --- a/Proxytrace.Storage/Internal/Entities/ApiKey/ApiKeyConfig.cs +++ b/Proxytrace.Storage/Internal/Entities/ApiKey/ApiKeyConfig.cs @@ -19,6 +19,9 @@ internal class ApiKeyConfig : AbstractEntityConfiguration, IMapper private readonly IRepository providers; private readonly IRepository users; + /// + /// Initializes a new instance of the class. + /// public ApiKeyConfig( IApiKey.CreateExisting factory, IRepository projects, @@ -31,6 +34,9 @@ public ApiKeyConfig( this.users = users; } + /// + /// Configures the application request pipeline. + /// public override void Configure(EntityTypeBuilder builder) { builder.HasIndex(e => e.KeyHash).IsUnique(); @@ -72,6 +78,9 @@ public override void Configure(EntityTypeBuilder builder) .OnDelete(DeleteBehavior.Restrict); } + /// + /// Maps. + /// public async Task Map(ApiKeyEntity stored, CancellationToken cancellationToken = default) { var project = await projects.GetAsync(stored.Project, cancellationToken); @@ -80,6 +89,9 @@ public async Task Map(ApiKeyEntity stored, CancellationToken cancellati return factory(stored.Name, stored.KeyHash, stored.KeyPrefix ?? string.Empty, project, provider, stored.Scopes, owner, stored); } + /// + /// Maps. + /// public Task Map(IApiKey domain, CancellationToken cancellationToken = default) => new ApiKeyEntity { diff --git a/Proxytrace.Storage/Internal/Entities/ApiKey/ApiKeyRepository.cs b/Proxytrace.Storage/Internal/Entities/ApiKey/ApiKeyRepository.cs index a04add4df..09cf69f9f 100644 --- a/Proxytrace.Storage/Internal/Entities/ApiKey/ApiKeyRepository.cs +++ b/Proxytrace.Storage/Internal/Entities/ApiKey/ApiKeyRepository.cs @@ -12,6 +12,9 @@ internal class ApiKeyRepository : AbstractRepository, IAp { private readonly ISecretHasher hasher; + /// + /// Initializes a new instance of the class. + /// public ApiKeyRepository( IMapper mapper, Func contextFactory, @@ -23,6 +26,9 @@ public ApiKeyRepository( this.hasher = hasher; } + /// + /// Finds the by key asynchronously. + /// public async Task FindByKeyAsync(string key, CancellationToken cancellationToken = default) { // The key is stored as a hash; match on the hash of the presented raw key. @@ -36,6 +42,9 @@ public ApiKeyRepository( return await Map(entity, cancellationToken); } + /// + /// Gets the by provider asynchronously. + /// public async Task> GetByProviderAsync(Guid providerId, CancellationToken cancellationToken = default) { var stored = await contextFactory() @@ -47,6 +56,9 @@ public async Task> GetByProviderAsync(Guid providerId, Ca return await Map(stored, cancellationToken); } + /// + /// Gets the by project asynchronously. + /// public async Task> GetByProjectAsync(Guid projectId, CancellationToken cancellationToken = default) { var stored = await contextFactory() @@ -58,6 +70,9 @@ public async Task> GetByProjectAsync(Guid projectId, Canc return await Map(stored, cancellationToken); } + /// + /// Gets the key names by owner asynchronously. + /// public async Task> GetKeyNamesByOwnerAsync(Guid ownerId, CancellationToken cancellationToken = default) // Projects the name column only — no mapping, so this never materializes a key entity (and // never resolves its project/provider/owner graph) just to answer "does this user own any?". diff --git a/Proxytrace.Storage/Internal/Entities/ApplicationError/ApplicationErrorConfig.cs b/Proxytrace.Storage/Internal/Entities/ApplicationError/ApplicationErrorConfig.cs index 277bb4fa6..6ee18f5b8 100644 --- a/Proxytrace.Storage/Internal/Entities/ApplicationError/ApplicationErrorConfig.cs +++ b/Proxytrace.Storage/Internal/Entities/ApplicationError/ApplicationErrorConfig.cs @@ -11,11 +11,17 @@ internal class ApplicationErrorConfig { private readonly IApplicationError.CreateExisting factory; + /// + /// Initializes a new instance of the class. + /// public ApplicationErrorConfig(IApplicationError.CreateExisting factory) { this.factory = factory; } + /// + /// Configures the application request pipeline. + /// public override void Configure(EntityTypeBuilder builder) { builder.HasIndex(e => e.CreatedAt); @@ -25,6 +31,9 @@ public override void Configure(EntityTypeBuilder builder // StackTrace is intentionally unbounded (Postgres text) — stacktraces are large. } + /// + /// Maps. + /// public Task Map(ApplicationErrorEntity stored, CancellationToken cancellationToken = default) => factory( stored.Message, @@ -34,6 +43,9 @@ public Task Map(ApplicationErrorEntity stored, CancellationTo stored.StackTrace, stored).ToTaskResult(); + /// + /// Maps. + /// public Task Map(IApplicationError domain, CancellationToken cancellationToken = default) => new ApplicationErrorEntity { diff --git a/Proxytrace.Storage/Internal/Entities/ApplicationError/ApplicationErrorEntity.cs b/Proxytrace.Storage/Internal/Entities/ApplicationError/ApplicationErrorEntity.cs index 2be20caf6..fdd46b940 100644 --- a/Proxytrace.Storage/Internal/Entities/ApplicationError/ApplicationErrorEntity.cs +++ b/Proxytrace.Storage/Internal/Entities/ApplicationError/ApplicationErrorEntity.cs @@ -5,13 +5,28 @@ namespace Proxytrace.Storage.Internal.Entities.ApplicationError; [StoredDomainEntity(typeof(IApplicationError))] internal record ApplicationErrorEntity : Entity { + /// + /// Gets or sets the message. + /// public required string Message { get; init; } + /// + /// Gets or sets the level. + /// public required ApplicationErrorLevel Level { get; init; } + /// + /// Gets or sets the category. + /// public required string Category { get; init; } + /// + /// Gets or sets the exception type. + /// public string? ExceptionType { get; init; } + /// + /// Gets or sets the stack trace. + /// public string? StackTrace { get; init; } } diff --git a/Proxytrace.Storage/Internal/Entities/ApplicationError/ApplicationErrorRepository.cs b/Proxytrace.Storage/Internal/Entities/ApplicationError/ApplicationErrorRepository.cs index 71988fa63..9e6d3f9c2 100644 --- a/Proxytrace.Storage/Internal/Entities/ApplicationError/ApplicationErrorRepository.cs +++ b/Proxytrace.Storage/Internal/Entities/ApplicationError/ApplicationErrorRepository.cs @@ -12,6 +12,9 @@ internal class ApplicationErrorRepository : AbstractRepository, IApplicationErrorRepository { + /// + /// Initializes a new instance of the class. + /// public ApplicationErrorRepository( IMapper mapper, Func contextFactory, @@ -21,6 +24,9 @@ public ApplicationErrorRepository( { } + /// + /// Gets the paged newest first asynchronously. + /// public async Task> GetPagedNewestFirstAsync( int page, int pageSize, @@ -72,6 +78,9 @@ public async Task> GetPagedNewestFirstAsync( return new PagedResult(items, total, page, pageSize); } + /// + /// Removes the older than asynchronously. + /// public async Task RemoveOlderThanAsync(DateTimeOffset cutoffDate, CancellationToken cancellationToken = default) { var context = contextFactory(); @@ -85,6 +94,9 @@ public async Task RemoveOlderThanAsync(DateTimeOffset cutoffDate, Cancellat return await context.SaveChangesAsync(cancellationToken); } + /// + /// Trims the to newest asynchronously. + /// public async Task TrimToNewestAsync(int max, CancellationToken cancellationToken = default) { var context = contextFactory(); diff --git a/Proxytrace.Storage/Internal/Entities/AuditLog/AuditLogEntryConfig.cs b/Proxytrace.Storage/Internal/Entities/AuditLog/AuditLogEntryConfig.cs index 7c3808aaa..8ae53d200 100644 --- a/Proxytrace.Storage/Internal/Entities/AuditLog/AuditLogEntryConfig.cs +++ b/Proxytrace.Storage/Internal/Entities/AuditLog/AuditLogEntryConfig.cs @@ -11,11 +11,17 @@ internal class AuditLogEntryConfig { private readonly IAuditLogEntry.CreateExisting factory; + /// + /// Initializes a new instance of the class. + /// public AuditLogEntryConfig(IAuditLogEntry.CreateExisting factory) { this.factory = factory; } + /// + /// Configures the application request pipeline. + /// public override void Configure(EntityTypeBuilder builder) { // Audit entries are denormalized, immutable snapshots. ActorUserId / ActorApiKeyId / @@ -31,6 +37,9 @@ public override void Configure(EntityTypeBuilder builder) // Details is intentionally unbounded (Postgres text) — pre-serialized JSON. } + /// + /// Maps. + /// public Task Map(AuditLogEntryEntity stored, CancellationToken cancellationToken = default) => factory( stored.Action, @@ -46,6 +55,9 @@ public Task Map(AuditLogEntryEntity stored, CancellationToken ca stored.Outcome, stored).ToTaskResult(); + /// + /// Maps. + /// public Task Map(IAuditLogEntry domain, CancellationToken cancellationToken = default) => new AuditLogEntryEntity { diff --git a/Proxytrace.Storage/Internal/Entities/AuditLog/AuditLogEntryEntity.cs b/Proxytrace.Storage/Internal/Entities/AuditLog/AuditLogEntryEntity.cs index 042d4f5a3..6f4699343 100644 --- a/Proxytrace.Storage/Internal/Entities/AuditLog/AuditLogEntryEntity.cs +++ b/Proxytrace.Storage/Internal/Entities/AuditLog/AuditLogEntryEntity.cs @@ -5,25 +5,58 @@ namespace Proxytrace.Storage.Internal.Entities.AuditLog; [StoredDomainEntity(typeof(IAuditLogEntry))] internal record AuditLogEntryEntity : Entity { + /// + /// Gets or sets the action. + /// public required AuditAction Action { get; init; } + /// + /// Gets or sets the actor type. + /// public required AuditActorType ActorType { get; init; } + /// + /// Gets or sets the actor user id. + /// public Guid? ActorUserId { get; init; } + /// + /// Gets or sets the actor email. + /// public string? ActorEmail { get; init; } + /// + /// Gets or sets the actor api key id. + /// public Guid? ActorApiKeyId { get; init; } + /// + /// Gets or sets the project id. + /// public Guid? ProjectId { get; init; } + /// + /// Gets or sets the target type. + /// public required string TargetType { get; init; } + /// + /// Gets or sets the target id. + /// public Guid? TargetId { get; init; } + /// + /// Gets or sets the target label. + /// public string? TargetLabel { get; init; } + /// + /// Gets or sets the details. + /// public string? Details { get; init; } + /// + /// Gets or sets the outcome. + /// public required AuditOutcome Outcome { get; init; } } diff --git a/Proxytrace.Storage/Internal/Entities/AuditLog/AuditLogRepository.cs b/Proxytrace.Storage/Internal/Entities/AuditLog/AuditLogRepository.cs index b17028571..26d5727e3 100644 --- a/Proxytrace.Storage/Internal/Entities/AuditLog/AuditLogRepository.cs +++ b/Proxytrace.Storage/Internal/Entities/AuditLog/AuditLogRepository.cs @@ -12,6 +12,9 @@ internal class AuditLogRepository : AbstractRepository, IAuditLogRepository { + /// + /// Initializes a new instance of the class. + /// public AuditLogRepository( IMapper mapper, Func contextFactory, @@ -21,6 +24,9 @@ public AuditLogRepository( { } + /// + /// Gets the paged newest first asynchronously. + /// public async Task> GetPagedNewestFirstAsync( int page, int pageSize, @@ -98,6 +104,9 @@ public async Task> GetPagedNewestFirstAsync( return new PagedResult(items, total, page, pageSize); } + /// + /// Removes the older than asynchronously. + /// public async Task RemoveOlderThanAsync(DateTimeOffset cutoffDate, CancellationToken cancellationToken = default) { var context = contextFactory(); diff --git a/Proxytrace.Storage/Internal/Entities/CostLimit/CostLimitConfig.cs b/Proxytrace.Storage/Internal/Entities/CostLimit/CostLimitConfig.cs index c10576910..0a3e91f4b 100644 --- a/Proxytrace.Storage/Internal/Entities/CostLimit/CostLimitConfig.cs +++ b/Proxytrace.Storage/Internal/Entities/CostLimit/CostLimitConfig.cs @@ -21,6 +21,9 @@ internal class CostLimitConfig private readonly IRepository apiKeys; private readonly ICostLimit.CreateExisting factory; + /// + /// Initializes a new instance of the class. + /// public CostLimitConfig( IRepository projects, IRepository agents, @@ -33,6 +36,9 @@ public CostLimitConfig( this.factory = factory; } + /// + /// Configures the application request pipeline. + /// public override void Configure(EntityTypeBuilder builder) { builder @@ -84,6 +90,9 @@ public override void Configure(EntityTypeBuilder builder) builder.HasIndex(e => e.Enabled); } + /// + /// Maps. + /// public async Task Map(CostLimitEntity storedEntity, CancellationToken cancellationToken = default) { // Sequential, NOT Task.WhenAll: inside a transaction every repository shares one @@ -111,6 +120,9 @@ public async Task Map(CostLimitEntity storedEntity, CancellationToke private async Task LoadApiKeyAsync(Guid? apiKeyId, CancellationToken cancellationToken) => apiKeyId is { } id ? await apiKeys.GetAsync(id, cancellationToken) : null; + /// + /// Maps. + /// public Task Map(ICostLimit domainEntity, CancellationToken cancellationToken = default) => new CostLimitEntity { diff --git a/Proxytrace.Storage/Internal/Entities/CostLimit/CostLimitEntity.cs b/Proxytrace.Storage/Internal/Entities/CostLimit/CostLimitEntity.cs index f3343d8d8..b351c84b1 100644 --- a/Proxytrace.Storage/Internal/Entities/CostLimit/CostLimitEntity.cs +++ b/Proxytrace.Storage/Internal/Entities/CostLimit/CostLimitEntity.cs @@ -5,6 +5,9 @@ namespace Proxytrace.Storage.Internal.Entities.CostLimit; [StoredDomainEntity(typeof(ICostLimit))] internal record CostLimitEntity : Entity { + /// + /// Gets or sets the project. + /// public required Guid Project { get; init; } /// The scoped agent, or null when the limit is not agent-scoped. @@ -13,9 +16,18 @@ internal record CostLimitEntity : Entity /// The scoped inbound API key, or null when the limit is not key-scoped. public Guid? ApiKey { get; init; } + /// + /// Gets or sets the soft limit eur. + /// public decimal? SoftLimitEur { get; init; } + /// + /// Gets or sets the hard limit eur. + /// public decimal? HardLimitEur { get; init; } + /// + /// Gets or sets the enabled. + /// public required bool Enabled { get; init; } } diff --git a/Proxytrace.Storage/Internal/Entities/CostLimit/CostLimitRepository.cs b/Proxytrace.Storage/Internal/Entities/CostLimit/CostLimitRepository.cs index 021adc03c..800f0ca93 100644 --- a/Proxytrace.Storage/Internal/Entities/CostLimit/CostLimitRepository.cs +++ b/Proxytrace.Storage/Internal/Entities/CostLimit/CostLimitRepository.cs @@ -11,6 +11,9 @@ internal class CostLimitRepository : AbstractRepository, ICostLimitRepository { + /// + /// Initializes a new instance of the class. + /// public CostLimitRepository( IMapper mapper, Func contextFactory, @@ -20,6 +23,9 @@ public CostLimitRepository( { } + /// + /// Gets the by project asynchronously. + /// public async Task> GetByProjectAsync( Guid projectId, CancellationToken cancellationToken = default) @@ -33,6 +39,9 @@ public async Task> GetByProjectAsync( return await Map(stored, cancellationToken); } + /// + /// Gets the all enabled asynchronously. + /// public async Task> GetAllEnabledAsync(CancellationToken cancellationToken = default) { var stored = await contextFactory() diff --git a/Proxytrace.Storage/Internal/Entities/CostLimitBreach/CostLimitBreachConfig.cs b/Proxytrace.Storage/Internal/Entities/CostLimitBreach/CostLimitBreachConfig.cs index f9d131450..ea8f3a15a 100644 --- a/Proxytrace.Storage/Internal/Entities/CostLimitBreach/CostLimitBreachConfig.cs +++ b/Proxytrace.Storage/Internal/Entities/CostLimitBreach/CostLimitBreachConfig.cs @@ -14,6 +14,9 @@ internal class CostLimitBreachConfig private readonly IRepository costLimits; private readonly ICostLimitBreach.CreateExisting factory; + /// + /// Initializes a new instance of the class. + /// public CostLimitBreachConfig( IRepository costLimits, ICostLimitBreach.CreateExisting factory) @@ -22,6 +25,9 @@ public CostLimitBreachConfig( this.factory = factory; } + /// + /// Configures the application request pipeline. + /// public override void Configure(EntityTypeBuilder builder) { builder @@ -42,6 +48,9 @@ public override void Configure(EntityTypeBuilder builder) builder.HasIndex(e => e.MonthStart); } + /// + /// Maps. + /// public async Task Map( CostLimitBreachEntity storedEntity, CancellationToken cancellationToken = default) @@ -52,6 +61,9 @@ public async Task Map( spendEur: storedEntity.SpendEur, existing: storedEntity); + /// + /// Maps. + /// public Task Map( ICostLimitBreach domainEntity, CancellationToken cancellationToken = default) diff --git a/Proxytrace.Storage/Internal/Entities/CostLimitBreach/CostLimitBreachEntity.cs b/Proxytrace.Storage/Internal/Entities/CostLimitBreach/CostLimitBreachEntity.cs index 6e50c3aef..4acf07f46 100644 --- a/Proxytrace.Storage/Internal/Entities/CostLimitBreach/CostLimitBreachEntity.cs +++ b/Proxytrace.Storage/Internal/Entities/CostLimitBreach/CostLimitBreachEntity.cs @@ -6,12 +6,21 @@ namespace Proxytrace.Storage.Internal.Entities.CostLimitBreach; [StoredDomainEntity(typeof(ICostLimitBreach))] internal record CostLimitBreachEntity : Entity { + /// + /// Gets or sets the cost limit. + /// public required Guid CostLimit { get; init; } /// Midnight UTC on the first of the month the breach belongs to. public required DateTimeOffset MonthStart { get; init; } + /// + /// Gets or sets the threshold. + /// public required CostThreshold Threshold { get; init; } + /// + /// Gets or sets the spend eur. + /// public required decimal SpendEur { get; init; } } diff --git a/Proxytrace.Storage/Internal/Entities/CostLimitBreach/CostLimitBreachRepository.cs b/Proxytrace.Storage/Internal/Entities/CostLimitBreach/CostLimitBreachRepository.cs index fe5173246..e84a25f50 100644 --- a/Proxytrace.Storage/Internal/Entities/CostLimitBreach/CostLimitBreachRepository.cs +++ b/Proxytrace.Storage/Internal/Entities/CostLimitBreach/CostLimitBreachRepository.cs @@ -13,6 +13,9 @@ internal class CostLimitBreachRepository : AbstractRepository, ICostLimitBreachRepository { + /// + /// Initializes a new instance of the class. + /// public CostLimitBreachRepository( IMapper mapper, Func contextFactory, @@ -22,6 +25,9 @@ public CostLimitBreachRepository( { } + /// + /// Gets the fired thresholds asynchronously. + /// public async Task> GetFiredThresholdsAsync( DateTimeOffset monthStart, Guid? projectId = null, @@ -53,6 +59,9 @@ public async Task> GetFiredThresholdsAsync( return await query.ToListAsync(cancellationToken); } + /// + /// Deletes the for limit asynchronously. + /// public async Task DeleteForLimitAsync(Guid costLimitId, CancellationToken cancellationToken = default) { var context = contextFactory(); @@ -73,6 +82,9 @@ public async Task DeleteForLimitAsync(Guid costLimitId, CancellationToken cancel Notify(costLimitId, EntityChangeType.Removed); } + /// + /// Gets the active hard blocks asynchronously. + /// public async Task> GetActiveHardBlocksAsync( Guid projectId, DateTimeOffset monthStart, diff --git a/Proxytrace.Storage/Internal/Entities/CustomAnomalyDetector/CustomAnomalyDetectorAgentConfig.cs b/Proxytrace.Storage/Internal/Entities/CustomAnomalyDetector/CustomAnomalyDetectorAgentConfig.cs index d0dd9deaa..a135019e0 100644 --- a/Proxytrace.Storage/Internal/Entities/CustomAnomalyDetector/CustomAnomalyDetectorAgentConfig.cs +++ b/Proxytrace.Storage/Internal/Entities/CustomAnomalyDetector/CustomAnomalyDetectorAgentConfig.cs @@ -6,6 +6,9 @@ namespace Proxytrace.Storage.Internal.Entities.CustomAnomalyDetector; internal class CustomAnomalyDetectorAgentConfig : AbstractEntityConfiguration { + /// + /// Configures the application request pipeline. + /// public override void Configure(EntityTypeBuilder builder) { builder.HasKey(e => new { e.DetectorId, e.AgentId }); diff --git a/Proxytrace.Storage/Internal/Entities/CustomAnomalyDetector/CustomAnomalyDetectorAgentEntity.cs b/Proxytrace.Storage/Internal/Entities/CustomAnomalyDetector/CustomAnomalyDetectorAgentEntity.cs index 9c6e19d7c..cb936f746 100644 --- a/Proxytrace.Storage/Internal/Entities/CustomAnomalyDetector/CustomAnomalyDetectorAgentEntity.cs +++ b/Proxytrace.Storage/Internal/Entities/CustomAnomalyDetector/CustomAnomalyDetectorAgentEntity.cs @@ -6,6 +6,12 @@ namespace Proxytrace.Storage.Internal.Entities.CustomAnomalyDetector; /// internal record CustomAnomalyDetectorAgentEntity { + /// + /// Gets or sets the detector id. + /// public required Guid DetectorId { get; init; } + /// + /// Gets or sets the agent id. + /// public required Guid AgentId { get; init; } } diff --git a/Proxytrace.Storage/Internal/Entities/CustomAnomalyDetector/CustomAnomalyDetectorConfig.cs b/Proxytrace.Storage/Internal/Entities/CustomAnomalyDetector/CustomAnomalyDetectorConfig.cs index 189ee0feb..ff372b79b 100644 --- a/Proxytrace.Storage/Internal/Entities/CustomAnomalyDetector/CustomAnomalyDetectorConfig.cs +++ b/Proxytrace.Storage/Internal/Entities/CustomAnomalyDetector/CustomAnomalyDetectorConfig.cs @@ -18,6 +18,9 @@ internal class CustomAnomalyDetectorConfig private readonly ISerializer serializer; private readonly Func contextFactory; + /// + /// Initializes a new instance of the class. + /// public CustomAnomalyDetectorConfig( IRepository agents, ICustomAnomalyDetector.CreateExisting factory, @@ -30,6 +33,9 @@ public CustomAnomalyDetectorConfig( this.contextFactory = contextFactory; } + /// + /// Configures the application request pipeline. + /// public override void Configure(EntityTypeBuilder builder) { // The hidden system agent owns the detector in the delete graph: removing the agent (e.g. @@ -48,6 +54,9 @@ public override void Configure(EntityTypeBuilder bu builder.Property(e => e.BlockUpstream).HasDefaultValue(false); } + /// + /// Maps. + /// public async Task Map( CustomAnomalyDetectorEntity storedEntity, CancellationToken cancellationToken = default) @@ -74,6 +83,9 @@ public async Task Map( existing: storedEntity); } + /// + /// Maps. + /// public Task Map( ICustomAnomalyDetector domainEntity, CancellationToken cancellationToken = default) diff --git a/Proxytrace.Storage/Internal/Entities/CustomAnomalyDetector/CustomAnomalyDetectorEntity.cs b/Proxytrace.Storage/Internal/Entities/CustomAnomalyDetector/CustomAnomalyDetectorEntity.cs index 46a43ad60..16b964072 100644 --- a/Proxytrace.Storage/Internal/Entities/CustomAnomalyDetector/CustomAnomalyDetectorEntity.cs +++ b/Proxytrace.Storage/Internal/Entities/CustomAnomalyDetector/CustomAnomalyDetectorEntity.cs @@ -5,11 +5,17 @@ namespace Proxytrace.Storage.Internal.Entities.CustomAnomalyDetector; [StoredDomainEntity(typeof(ICustomAnomalyDetector))] internal record CustomAnomalyDetectorEntity : Entity { + /// + /// Gets or sets the name. + /// public required string Name { get; init; } /// The hidden system agent holding the review instructions as its system prompt. public required Guid Agent { get; init; } + /// + /// Gets or sets the project. + /// public required Guid Project { get; init; } /// @@ -18,12 +24,21 @@ internal record CustomAnomalyDetectorEntity : Entity /// public required string Triggers { get; init; } + /// + /// Gets or sets the all agents. + /// public required bool AllAgents { get; init; } + /// + /// Gets or sets the is enabled. + /// public required bool IsEnabled { get; init; } /// Whether the proxy rejects trigger-matching requests before they reach the provider. public required bool BlockUpstream { get; init; } + /// + /// Gets or sets the scoped agents. + /// public required ICollection ScopedAgents { get; init; } } diff --git a/Proxytrace.Storage/Internal/Entities/CustomAnomalyDetector/CustomAnomalyDetectorRepository.cs b/Proxytrace.Storage/Internal/Entities/CustomAnomalyDetector/CustomAnomalyDetectorRepository.cs index d8a08a5d6..bceb494e8 100644 --- a/Proxytrace.Storage/Internal/Entities/CustomAnomalyDetector/CustomAnomalyDetectorRepository.cs +++ b/Proxytrace.Storage/Internal/Entities/CustomAnomalyDetector/CustomAnomalyDetectorRepository.cs @@ -16,6 +16,9 @@ internal class CustomAnomalyDetectorRepository { private readonly ISerializer serializer; + /// + /// Initializes a new instance of the class. + /// public CustomAnomalyDetectorRepository( IMapper mapper, ISerializer serializer, @@ -27,6 +30,9 @@ public CustomAnomalyDetectorRepository( this.serializer = serializer; } + /// + /// Gets the by project asynchronously. + /// public async Task> GetByProjectAsync( Guid projectId, CancellationToken cancellationToken = default) @@ -40,6 +46,9 @@ public async Task> GetByProjectAsync( return await Map(stored, cancellationToken); } + /// + /// Gets the enabled by project asynchronously. + /// public async Task> GetEnabledByProjectAsync( Guid projectId, CancellationToken cancellationToken = default) @@ -53,6 +62,9 @@ public async Task> GetEnabledByProjectAsyn return await Map(stored, cancellationToken); } + /// + /// Gets the enabled blocking rules by project asynchronously. + /// public async Task> GetEnabledBlockingRulesByProjectAsync( Guid projectId, CancellationToken cancellationToken = default) diff --git a/Proxytrace.Storage/Internal/Entities/CustomAnomalyResult/CustomAnomalyResultConfig.cs b/Proxytrace.Storage/Internal/Entities/CustomAnomalyResult/CustomAnomalyResultConfig.cs index e2b159111..8caae93a9 100644 --- a/Proxytrace.Storage/Internal/Entities/CustomAnomalyResult/CustomAnomalyResultConfig.cs +++ b/Proxytrace.Storage/Internal/Entities/CustomAnomalyResult/CustomAnomalyResultConfig.cs @@ -13,11 +13,17 @@ internal class CustomAnomalyResultConfig { private readonly ICustomAnomalyResult.CreateExisting factory; + /// + /// Initializes a new instance of the class. + /// public CustomAnomalyResultConfig(ICustomAnomalyResult.CreateExisting factory) { this.factory = factory; } + /// + /// Configures the application request pipeline. + /// public override void Configure(EntityTypeBuilder builder) { // Results are owned attribution rows: they never outlive their detector or their call @@ -39,6 +45,9 @@ public override void Configure(EntityTypeBuilder buil builder.HasIndex(e => new { e.ProjectId, e.CreatedAt }).IsDescending(false, true); } + /// + /// Maps. + /// public Task Map( CustomAnomalyResultEntity storedEntity, CancellationToken cancellationToken = default) @@ -50,6 +59,9 @@ public Task Map( reasoning: storedEntity.Reasoning, existing: storedEntity).ToTaskResult(); + /// + /// Maps. + /// public Task Map( ICustomAnomalyResult domainEntity, CancellationToken cancellationToken = default) diff --git a/Proxytrace.Storage/Internal/Entities/CustomAnomalyResult/CustomAnomalyResultEntity.cs b/Proxytrace.Storage/Internal/Entities/CustomAnomalyResult/CustomAnomalyResultEntity.cs index bba494193..30102d06a 100644 --- a/Proxytrace.Storage/Internal/Entities/CustomAnomalyResult/CustomAnomalyResultEntity.cs +++ b/Proxytrace.Storage/Internal/Entities/CustomAnomalyResult/CustomAnomalyResultEntity.cs @@ -5,9 +5,24 @@ namespace Proxytrace.Storage.Internal.Entities.CustomAnomalyResult; [StoredDomainEntity(typeof(ICustomAnomalyResult))] internal record CustomAnomalyResultEntity : Entity { + /// + /// Gets or sets the detector id. + /// public required Guid DetectorId { get; init; } + /// + /// Gets or sets the agent call id. + /// public required Guid AgentCallId { get; init; } + /// + /// Gets or sets the project id. + /// public required Guid ProjectId { get; init; } + /// + /// Gets or sets the matched trigger. + /// public required string MatchedTrigger { get; init; } + /// + /// Gets or sets the reasoning. + /// public string? Reasoning { get; init; } } diff --git a/Proxytrace.Storage/Internal/Entities/CustomAnomalyResult/CustomAnomalyResultRepository.cs b/Proxytrace.Storage/Internal/Entities/CustomAnomalyResult/CustomAnomalyResultRepository.cs index 728de0094..7bcdfb3f6 100644 --- a/Proxytrace.Storage/Internal/Entities/CustomAnomalyResult/CustomAnomalyResultRepository.cs +++ b/Proxytrace.Storage/Internal/Entities/CustomAnomalyResult/CustomAnomalyResultRepository.cs @@ -11,6 +11,9 @@ internal class CustomAnomalyResultRepository : AbstractRepository, ICustomAnomalyResultRepository { + /// + /// Initializes a new instance of the class. + /// public CustomAnomalyResultRepository( IMapper mapper, Func contextFactory, @@ -20,6 +23,9 @@ public CustomAnomalyResultRepository( { } + /// + /// Gets the by agent call ids asynchronously. + /// public async Task> GetByAgentCallIdsAsync( IReadOnlyCollection agentCallIds, CancellationToken cancellationToken = default) diff --git a/Proxytrace.Storage/Internal/Entities/EmailSettings/EmailSettingsConfig.cs b/Proxytrace.Storage/Internal/Entities/EmailSettings/EmailSettingsConfig.cs index fb93c5ee7..dd1e9173c 100644 --- a/Proxytrace.Storage/Internal/Entities/EmailSettings/EmailSettingsConfig.cs +++ b/Proxytrace.Storage/Internal/Entities/EmailSettings/EmailSettingsConfig.cs @@ -4,6 +4,9 @@ namespace Proxytrace.Storage.Internal.Entities.EmailSettings; internal class EmailSettingsConfig : AbstractEntityConfiguration { + /// + /// Configures the application request pipeline. + /// public override void Configure(EntityTypeBuilder builder) { } diff --git a/Proxytrace.Storage/Internal/Entities/EmailSettings/EmailSettingsEntity.cs b/Proxytrace.Storage/Internal/Entities/EmailSettings/EmailSettingsEntity.cs index 6384c77c5..e4e668502 100644 --- a/Proxytrace.Storage/Internal/Entities/EmailSettings/EmailSettingsEntity.cs +++ b/Proxytrace.Storage/Internal/Entities/EmailSettings/EmailSettingsEntity.cs @@ -9,14 +9,44 @@ namespace Proxytrace.Storage.Internal.Entities.EmailSettings; /// internal record EmailSettingsEntity : Entity { + /// + /// Gets or sets the enabled. + /// public required bool Enabled { get; init; } + /// + /// Gets or sets the smtp host. + /// public required string SmtpHost { get; init; } + /// + /// Gets or sets the smtp port. + /// public required int SmtpPort { get; init; } + /// + /// Gets or sets the security. + /// public required SmtpSecurity Security { get; init; } + /// + /// Gets or sets the username. + /// public string? Username { get; init; } + /// + /// Gets or sets the password. + /// public string? Password { get; init; } + /// + /// Gets or sets the from address. + /// public required string FromAddress { get; init; } + /// + /// Gets or sets the from name. + /// public required string FromName { get; init; } + /// + /// Gets or sets the app base url. + /// public string? AppBaseUrl { get; init; } + /// + /// Gets or sets the min severity. + /// public required NotificationSeverity MinSeverity { get; init; } } diff --git a/Proxytrace.Storage/Internal/Entities/EmailSettings/EmailSettingsStore.cs b/Proxytrace.Storage/Internal/Entities/EmailSettings/EmailSettingsStore.cs index 1d9ac40a8..60a039709 100644 --- a/Proxytrace.Storage/Internal/Entities/EmailSettings/EmailSettingsStore.cs +++ b/Proxytrace.Storage/Internal/Entities/EmailSettings/EmailSettingsStore.cs @@ -17,6 +17,9 @@ internal sealed class EmailSettingsStore : IEmailSettingsStore private readonly IClock clock; private readonly ILogger logger; + /// + /// Initializes a new instance of the class. + /// public EmailSettingsStore( Func contextFactory, ISecretProtector secretProtector, @@ -29,6 +32,9 @@ public EmailSettingsStore( this.logger = logger; } + /// + /// Gets asynchronously. + /// public async Task GetAsync(CancellationToken cancellationToken = default) { EmailSettingsEntity? entity = await contextFactory() @@ -73,6 +79,9 @@ public EmailSettingsStore( } } + /// + /// Saves asynchronously. + /// public async Task SaveAsync(AppEmailSettings settings, CancellationToken cancellationToken = default) { var cipher = string.IsNullOrEmpty(settings.Password) diff --git a/Proxytrace.Storage/Internal/Entities/Evaluator/EvaluatorConfig.cs b/Proxytrace.Storage/Internal/Entities/Evaluator/EvaluatorConfig.cs index fd6d8eaae..d5bbdd447 100644 --- a/Proxytrace.Storage/Internal/Entities/Evaluator/EvaluatorConfig.cs +++ b/Proxytrace.Storage/Internal/Entities/Evaluator/EvaluatorConfig.cs @@ -20,6 +20,9 @@ internal class EvaluatorConfig : AbstractEntityConfiguration, I private readonly IRepository projects; private readonly ISerializer serializer; + /// + /// Initializes a new instance of the class. + /// public EvaluatorConfig( IExactMatchEvaluator.CreateExisting createExactMatch, IAgenticEvaluator.CreateExisting createAgentic, @@ -39,6 +42,9 @@ public EvaluatorConfig( this.serializer = serializer; } + /// + /// Configures the application request pipeline. + /// public override void Configure(EntityTypeBuilder builder) { builder.HasIndex(e => e.Kind); @@ -46,6 +52,9 @@ public override void Configure(EntityTypeBuilder builder) builder.HasIndex(e => new { e.Project, e.IsArchived }); } + /// + /// Maps. + /// public async Task Map(EvaluatorEntity stored, CancellationToken cancellationToken = default) { IProject project = await projects.GetAsync(stored.Project, cancellationToken); @@ -78,6 +87,9 @@ private INumericMatchEvaluator MapNumericMatch(EvaluatorEntity stored, IProject return createNumericMatch(new Regex(data.ExtractionPattern), data.Tolerance, project, stored); } + /// + /// Maps. + /// public Task Map(IEvaluator domain, CancellationToken cancellationToken = default) { string data = domain switch diff --git a/Proxytrace.Storage/Internal/Entities/Evaluator/EvaluatorEntity.cs b/Proxytrace.Storage/Internal/Entities/Evaluator/EvaluatorEntity.cs index 1bb695660..765f47fc1 100644 --- a/Proxytrace.Storage/Internal/Entities/Evaluator/EvaluatorEntity.cs +++ b/Proxytrace.Storage/Internal/Entities/Evaluator/EvaluatorEntity.cs @@ -11,6 +11,9 @@ internal record EvaluatorEntity : Entity, IArchivableEntity /// public required EvaluatorKind Kind { get; init; } + /// + /// Gets or sets the project. + /// public required Guid Project { get; init; } /// diff --git a/Proxytrace.Storage/Internal/Entities/Evaluator/EvaluatorRepository.cs b/Proxytrace.Storage/Internal/Entities/Evaluator/EvaluatorRepository.cs index 39e7fa67b..4e5ed45f9 100644 --- a/Proxytrace.Storage/Internal/Entities/Evaluator/EvaluatorRepository.cs +++ b/Proxytrace.Storage/Internal/Entities/Evaluator/EvaluatorRepository.cs @@ -10,6 +10,9 @@ namespace Proxytrace.Storage.Internal.Entities.Evaluator; [UsedImplicitly] internal class EvaluatorRepository : ArchivableRepository, IEvaluatorRepository { + /// + /// Initializes a new instance of the class. + /// public EvaluatorRepository( IMapper mapper, Func contextFactory, @@ -19,6 +22,9 @@ public EvaluatorRepository( { } + /// + /// Gets the by project asynchronously. + /// public async Task> GetByProjectAsync(Guid projectId, CancellationToken cancellationToken = default) { var stored = await contextFactory() @@ -31,6 +37,9 @@ public async Task> GetByProjectAsync(Guid projectId, C return await Map(stored, cancellationToken); } + /// + /// Gets the project id asynchronously. + /// public async Task GetProjectIdAsync(Guid evaluatorId, CancellationToken cancellationToken = default) => await contextFactory() .Set() diff --git a/Proxytrace.Storage/Internal/Entities/Inference/ModelParametersData.cs b/Proxytrace.Storage/Internal/Entities/Inference/ModelParametersData.cs index b4ba9be0e..a204ec43a 100644 --- a/Proxytrace.Storage/Internal/Entities/Inference/ModelParametersData.cs +++ b/Proxytrace.Storage/Internal/Entities/Inference/ModelParametersData.cs @@ -14,5 +14,8 @@ internal record ModelParametersData( IReadOnlyList? Stop, int? N) { + /// + /// Gets the empty. + /// public static ModelParametersData Empty { get; } = new(null, null, null, null, null, null, null, null, null); } diff --git a/Proxytrace.Storage/Internal/Entities/Invite/InviteConfig.cs b/Proxytrace.Storage/Internal/Entities/Invite/InviteConfig.cs index 1968084e5..7171e900f 100644 --- a/Proxytrace.Storage/Internal/Entities/Invite/InviteConfig.cs +++ b/Proxytrace.Storage/Internal/Entities/Invite/InviteConfig.cs @@ -13,12 +13,18 @@ internal class InviteConfig : AbstractEntityConfiguration, IMapper private readonly IInvite.CreateExisting factory; private readonly IRepository users; + /// + /// Initializes a new instance of the class. + /// public InviteConfig(IInvite.CreateExisting factory, IRepository users) { this.factory = factory; this.users = users; } + /// + /// Configures the application request pipeline. + /// public override void Configure(EntityTypeBuilder builder) { builder.HasIndex(e => e.TokenHash).IsUnique(); @@ -30,12 +36,18 @@ public override void Configure(EntityTypeBuilder builder) .OnDelete(DeleteBehavior.Restrict); } + /// + /// Maps. + /// public async Task Map(InviteEntity stored, CancellationToken cancellationToken = default) { var inviter = await users.GetAsync(stored.InvitedBy, cancellationToken); return factory(stored.Email, stored.Role, stored.TokenHash, stored.ExpiresAt, stored.ConsumedAt, inviter, stored); } + /// + /// Maps. + /// public Task Map(IInvite domain, CancellationToken cancellationToken = default) => new InviteEntity { diff --git a/Proxytrace.Storage/Internal/Entities/Invite/InviteEntity.cs b/Proxytrace.Storage/Internal/Entities/Invite/InviteEntity.cs index cdc54a522..15a23362e 100644 --- a/Proxytrace.Storage/Internal/Entities/Invite/InviteEntity.cs +++ b/Proxytrace.Storage/Internal/Entities/Invite/InviteEntity.cs @@ -6,7 +6,13 @@ namespace Proxytrace.Storage.Internal.Entities.Invite; [StoredDomainEntity(typeof(IInvite))] internal record InviteEntity : Entity { + /// + /// Gets or sets the email. + /// public required string Email { get; init; } + /// + /// Gets or sets the role. + /// public required UserRole Role { get; init; } /// @@ -14,7 +20,16 @@ internal record InviteEntity : Entity /// so only its hash is stored). /// public required string TokenHash { get; init; } + /// + /// Gets or sets the expires at. + /// public required DateTimeOffset ExpiresAt { get; init; } + /// + /// Gets or sets the consumed at. + /// public DateTimeOffset? ConsumedAt { get; init; } + /// + /// Gets or sets the invited by. + /// public required Guid InvitedBy { get; init; } } diff --git a/Proxytrace.Storage/Internal/Entities/Invite/InviteRepository.cs b/Proxytrace.Storage/Internal/Entities/Invite/InviteRepository.cs index 91990f2b4..6b62ec753 100644 --- a/Proxytrace.Storage/Internal/Entities/Invite/InviteRepository.cs +++ b/Proxytrace.Storage/Internal/Entities/Invite/InviteRepository.cs @@ -12,6 +12,9 @@ internal class InviteRepository : AbstractRepository, IIn { private readonly ISecretHasher hasher; + /// + /// Initializes a new instance of the class. + /// public InviteRepository( IMapper mapper, Func context, @@ -23,6 +26,9 @@ public InviteRepository( this.hasher = hasher; } + /// + /// Finds the by token asynchronously. + /// public async Task FindByTokenAsync(string token, CancellationToken cancellationToken = default) { // The token is stored as a hash; match on the hash of the presented raw token. diff --git a/Proxytrace.Storage/Internal/Entities/Licensing/StoredLicenseConfig.cs b/Proxytrace.Storage/Internal/Entities/Licensing/StoredLicenseConfig.cs index 5870cc48a..04f0ad7f3 100644 --- a/Proxytrace.Storage/Internal/Entities/Licensing/StoredLicenseConfig.cs +++ b/Proxytrace.Storage/Internal/Entities/Licensing/StoredLicenseConfig.cs @@ -4,6 +4,9 @@ namespace Proxytrace.Storage.Internal.Entities.Licensing; internal class StoredLicenseConfig : AbstractEntityConfiguration { + /// + /// Configures the application request pipeline. + /// public override void Configure(EntityTypeBuilder builder) { } diff --git a/Proxytrace.Storage/Internal/Entities/Licensing/StoredLicenseEntity.cs b/Proxytrace.Storage/Internal/Entities/Licensing/StoredLicenseEntity.cs index 77de53d79..f0c4f2252 100644 --- a/Proxytrace.Storage/Internal/Entities/Licensing/StoredLicenseEntity.cs +++ b/Proxytrace.Storage/Internal/Entities/Licensing/StoredLicenseEntity.cs @@ -6,5 +6,8 @@ namespace Proxytrace.Storage.Internal.Entities.Licensing; /// internal record StoredLicenseEntity : Entity { + /// + /// Gets or sets the jwt. + /// public required string Jwt { get; init; } } diff --git a/Proxytrace.Storage/Internal/Entities/Licensing/StoredLicenseStore.cs b/Proxytrace.Storage/Internal/Entities/Licensing/StoredLicenseStore.cs index 1ab3341b1..33aa5b5ec 100644 --- a/Proxytrace.Storage/Internal/Entities/Licensing/StoredLicenseStore.cs +++ b/Proxytrace.Storage/Internal/Entities/Licensing/StoredLicenseStore.cs @@ -11,12 +11,18 @@ internal sealed class StoredLicenseStore : IStoredLicenseStore private readonly Func contextFactory; private readonly IClock clock; + /// + /// Initializes a new instance of the class. + /// public StoredLicenseStore(Func contextFactory, IClock clock) { this.contextFactory = contextFactory; this.clock = clock; } + /// + /// Gets asynchronously. + /// public async Task GetAsync(CancellationToken cancellationToken = default) { StoredLicenseEntity? entity = await contextFactory() @@ -26,6 +32,9 @@ public StoredLicenseStore(Func contextFactory, IClock clock) return entity?.Jwt; } + /// + /// Saves asynchronously. + /// public async Task SaveAsync(string licenseJwt, CancellationToken cancellationToken = default) { ArgumentException.ThrowIfNullOrWhiteSpace(licenseJwt); @@ -54,6 +63,9 @@ public async Task SaveAsync(string licenseJwt, CancellationToken cancellationTok await context.SaveChangesAsync(cancellationToken); } + /// + /// Removes asynchronously. + /// public async Task RemoveAsync(CancellationToken cancellationToken = default) { StorageDbContext context = contextFactory(); diff --git a/Proxytrace.Storage/Internal/Entities/MfaBackupCode/MfaBackupCodeConfig.cs b/Proxytrace.Storage/Internal/Entities/MfaBackupCode/MfaBackupCodeConfig.cs index 8fe5821e1..0d5fd9596 100644 --- a/Proxytrace.Storage/Internal/Entities/MfaBackupCode/MfaBackupCodeConfig.cs +++ b/Proxytrace.Storage/Internal/Entities/MfaBackupCode/MfaBackupCodeConfig.cs @@ -15,12 +15,18 @@ internal class MfaBackupCodeConfig private readonly IMfaBackupCode.CreateExisting factory; private readonly IRepository users; + /// + /// Initializes a new instance of the class. + /// public MfaBackupCodeConfig(IMfaBackupCode.CreateExisting factory, IRepository users) { this.factory = factory; this.users = users; } + /// + /// Configures the application request pipeline. + /// public override void Configure(EntityTypeBuilder builder) { builder.HasIndex(e => e.CodeHash).IsUnique(); @@ -36,12 +42,18 @@ public override void Configure(EntityTypeBuilder builder) .OnDelete(DeleteBehavior.Cascade); } + /// + /// Maps. + /// public async Task Map(MfaBackupCodeEntity stored, CancellationToken cancellationToken = default) { var user = await users.GetAsync(stored.User, cancellationToken); return factory(user, stored.CodeHash, stored.ConsumedAt, stored); } + /// + /// Maps. + /// public Task Map(IMfaBackupCode domain, CancellationToken cancellationToken = default) => new MfaBackupCodeEntity { diff --git a/Proxytrace.Storage/Internal/Entities/MfaBackupCode/MfaBackupCodeEntity.cs b/Proxytrace.Storage/Internal/Entities/MfaBackupCode/MfaBackupCodeEntity.cs index 186a1558b..b071e9dda 100644 --- a/Proxytrace.Storage/Internal/Entities/MfaBackupCode/MfaBackupCodeEntity.cs +++ b/Proxytrace.Storage/Internal/Entities/MfaBackupCode/MfaBackupCodeEntity.cs @@ -5,10 +5,16 @@ namespace Proxytrace.Storage.Internal.Entities.MfaBackupCode; [StoredDomainEntity(typeof(IMfaBackupCode))] internal record MfaBackupCodeEntity : Entity { + /// + /// Gets or sets the user. + /// public required Guid User { get; init; } /// — SHA-256 of the raw code (verify-only). public required string CodeHash { get; init; } + /// + /// Gets or sets the consumed at. + /// public DateTimeOffset? ConsumedAt { get; init; } } diff --git a/Proxytrace.Storage/Internal/Entities/MfaBackupCode/MfaBackupCodeRepository.cs b/Proxytrace.Storage/Internal/Entities/MfaBackupCode/MfaBackupCodeRepository.cs index faaaa23da..5a0210f03 100644 --- a/Proxytrace.Storage/Internal/Entities/MfaBackupCode/MfaBackupCodeRepository.cs +++ b/Proxytrace.Storage/Internal/Entities/MfaBackupCode/MfaBackupCodeRepository.cs @@ -11,6 +11,9 @@ internal class MfaBackupCodeRepository : AbstractRepository, IMfaBackupCodeRepository { + /// + /// Initializes a new instance of the class. + /// public MfaBackupCodeRepository( IMapper mapper, Func context, @@ -20,6 +23,9 @@ public MfaBackupCodeRepository( { } + /// + /// Lists the by user asynchronously. + /// public async Task> ListByUserAsync(Guid userId, CancellationToken cancellationToken = default) { var stored = await contextFactory().Set().AsNoTracking() @@ -28,6 +34,9 @@ public async Task> ListByUserAsync(Guid userId, Ca return await Map(stored, cancellationToken); } + /// + /// Finds the by code hash asynchronously. + /// public async Task FindByCodeHashAsync(Guid userId, string codeHash, CancellationToken cancellationToken = default) { var entity = await contextFactory().Set().AsNoTracking() diff --git a/Proxytrace.Storage/Internal/Entities/Model/ModelConfig.cs b/Proxytrace.Storage/Internal/Entities/Model/ModelConfig.cs index 59bf7ed2c..6b7aaafa1 100644 --- a/Proxytrace.Storage/Internal/Entities/Model/ModelConfig.cs +++ b/Proxytrace.Storage/Internal/Entities/Model/ModelConfig.cs @@ -8,20 +8,32 @@ internal class ModelConfig : AbstractEntityConfiguration, IMapper + /// Initializes a new instance of the class. + /// public ModelConfig(IModel.CreateExisting factory) { this.factory = factory; } + /// + /// Configures the application request pipeline. + /// public override void Configure(EntityTypeBuilder builder) { builder.HasIndex(e => e.Name).IsUnique(); builder.Property(e => e.Name).HasMaxLength(256).IsRequired(); } + /// + /// Maps. + /// public Task Map(ModelEntity stored, CancellationToken cancellationToken = default) => factory(stored.Name, stored).ToTaskResult(); + /// + /// Maps. + /// public Task Map(IModel domain, CancellationToken cancellationToken = default) => new ModelEntity { diff --git a/Proxytrace.Storage/Internal/Entities/Model/ModelRepository.cs b/Proxytrace.Storage/Internal/Entities/Model/ModelRepository.cs index 61a573382..2e580512f 100644 --- a/Proxytrace.Storage/Internal/Entities/Model/ModelRepository.cs +++ b/Proxytrace.Storage/Internal/Entities/Model/ModelRepository.cs @@ -13,6 +13,9 @@ internal class ModelRepository : AbstractRepository, IModel private readonly IModel.CreateNew factory; private readonly IAsyncLock locker; + /// + /// Initializes a new instance of the class. + /// public ModelRepository( IMapper mapper, IModel.CreateNew factory, @@ -27,6 +30,9 @@ public ModelRepository( this.locker = locker; } + /// + /// Gets the or create asynchronously. + /// public async Task GetOrCreateAsync(string name, CancellationToken cancellationToken = default) { var context = contextFactory(); diff --git a/Proxytrace.Storage/Internal/Entities/ModelEndpoint/ModelEndpointConfig.cs b/Proxytrace.Storage/Internal/Entities/ModelEndpoint/ModelEndpointConfig.cs index 5f6273c9f..da0101207 100644 --- a/Proxytrace.Storage/Internal/Entities/ModelEndpoint/ModelEndpointConfig.cs +++ b/Proxytrace.Storage/Internal/Entities/ModelEndpoint/ModelEndpointConfig.cs @@ -16,6 +16,9 @@ internal class ModelEndpointConfig : AbstractEntityConfiguration models; private readonly IRepository providers; + /// + /// Initializes a new instance of the class. + /// public ModelEndpointConfig( IModelEndpoint.CreateExisting factory, IRepository models, @@ -26,6 +29,9 @@ public ModelEndpointConfig( this.providers = providers; } + /// + /// Configures the application request pipeline. + /// public override void Configure(EntityTypeBuilder builder) { builder.Property(e => e.InputTokenCost).HasPrecision(18, 6).IsRequired(false); @@ -54,6 +60,9 @@ public override void Configure(EntityTypeBuilder builder) builder.HasIndex(e => new { e.Provider, e.IsArchived }); } + /// + /// Maps. + /// public async Task Map(ModelEndpointEntity stored, CancellationToken cancellationToken = default) { var model = await models.GetAsync(stored.Model, cancellationToken); @@ -61,6 +70,9 @@ public async Task Map(ModelEndpointEntity stored, CancellationTo return factory(model, provider, stored.InputTokenCost, stored.OutputTokenCost, stored.CachedInputTokenCost, stored); } + /// + /// Maps. + /// public Task Map(IModelEndpoint domain, CancellationToken cancellationToken = default) => new ModelEndpointEntity { diff --git a/Proxytrace.Storage/Internal/Entities/ModelEndpoint/ModelEndpointRepository.cs b/Proxytrace.Storage/Internal/Entities/ModelEndpoint/ModelEndpointRepository.cs index f53f14a4d..34100fe4f 100644 --- a/Proxytrace.Storage/Internal/Entities/ModelEndpoint/ModelEndpointRepository.cs +++ b/Proxytrace.Storage/Internal/Entities/ModelEndpoint/ModelEndpointRepository.cs @@ -17,6 +17,9 @@ internal class ModelEndpointRepository : ArchivableRepository + /// Initializes a new instance of the class. + /// public ModelEndpointRepository( IMapper mapper, Func contextFactory, @@ -38,6 +41,9 @@ public ModelEndpointRepository( // AgentCallConfig is the DB-level backstop. protected override bool SupportsHardDelete => false; + /// + /// Gets the or create asynchronously. + /// public async Task GetOrCreateAsync( string modelName, IModelProvider provider, @@ -87,6 +93,9 @@ public async Task GetOrCreateAsync( } } + /// + /// Gets the by provider asynchronously. + /// public async Task> GetByProviderAsync( Guid providerId, CancellationToken cancellationToken = default) diff --git a/Proxytrace.Storage/Internal/Entities/ModelProvider/ModelProviderConfig.cs b/Proxytrace.Storage/Internal/Entities/ModelProvider/ModelProviderConfig.cs index 23afe5abe..c423eb7ad 100644 --- a/Proxytrace.Storage/Internal/Entities/ModelProvider/ModelProviderConfig.cs +++ b/Proxytrace.Storage/Internal/Entities/ModelProvider/ModelProviderConfig.cs @@ -23,6 +23,9 @@ internal class ModelProviderConfig : AbstractEntityConfiguration logger; + /// + /// Initializes a new instance of the class. + /// public ModelProviderConfig( IModelProvider.CreateExisting factory, Lazy protector, @@ -35,6 +38,9 @@ public ModelProviderConfig( this.logger = logger; } + /// + /// Configures the application request pipeline. + /// public override void Configure(EntityTypeBuilder builder) { builder.HasIndex(e => e.Name).IsUnique(); @@ -52,9 +58,15 @@ public override void Configure(EntityTypeBuilder builder) builder.HasIndex(e => e.IsArchived); } + /// + /// Maps. + /// public Task Map(ModelProviderEntity stored, CancellationToken cancellationToken = default) => factory(stored.Name, new Uri(stored.Endpoint), Decrypt(stored.ApiKey), stored.Kind, stored).ToTaskResult(); + /// + /// Maps. + /// public Task Map(IModelProvider domain, CancellationToken cancellationToken = default) => new ModelProviderEntity { diff --git a/Proxytrace.Storage/Internal/Entities/ModelProvider/ModelProviderRepository.cs b/Proxytrace.Storage/Internal/Entities/ModelProvider/ModelProviderRepository.cs index 2f8b8bc3f..ed17f824a 100644 --- a/Proxytrace.Storage/Internal/Entities/ModelProvider/ModelProviderRepository.cs +++ b/Proxytrace.Storage/Internal/Entities/ModelProvider/ModelProviderRepository.cs @@ -13,6 +13,9 @@ internal class ModelProviderRepository : ArchivableRepository + /// Initializes a new instance of the class. + /// public ModelProviderRepository( IMapper mapper, Func contextFactory, @@ -30,6 +33,9 @@ public ModelProviderRepository( // too); RemoveAsync is refused. The FK Restrict in ModelEndpointConfig is the DB-level backstop. protected override bool SupportsHardDelete => false; + /// + /// Finds the by api key asynchronously. + /// public async Task FindByApiKeyAsync(string apiKey, CancellationToken cancellationToken = default) { // The plaintext key is encrypted (non-deterministic) at rest, so match on its deterministic diff --git a/Proxytrace.Storage/Internal/Entities/Notification/NotificationConfig.cs b/Proxytrace.Storage/Internal/Entities/Notification/NotificationConfig.cs index 794324ffd..b98083c54 100644 --- a/Proxytrace.Storage/Internal/Entities/Notification/NotificationConfig.cs +++ b/Proxytrace.Storage/Internal/Entities/Notification/NotificationConfig.cs @@ -12,11 +12,17 @@ internal class NotificationConfig : { private readonly INotification.CreateExisting factory; + /// + /// Initializes a new instance of the class. + /// public NotificationConfig(INotification.CreateExisting factory) { this.factory = factory; } + /// + /// Configures the application request pipeline. + /// public override void Configure(EntityTypeBuilder builder) { // Cascade with the owning project. ProjectId is nullable, so EF treats the relationship as @@ -36,6 +42,9 @@ public override void Configure(EntityTypeBuilder builder) builder.HasIndex(e => new { e.TargetKind, e.TargetId }); } + /// + /// Maps. + /// public Task Map(NotificationEntity stored, CancellationToken cancellationToken = default) => factory( stored.Kind, @@ -48,6 +57,9 @@ public Task Map(NotificationEntity stored, CancellationToken canc stored.TargetId, stored).ToTaskResult(); + /// + /// Maps. + /// public Task Map(INotification domain, CancellationToken cancellationToken = default) => new NotificationEntity { diff --git a/Proxytrace.Storage/Internal/Entities/Notification/NotificationRepository.cs b/Proxytrace.Storage/Internal/Entities/Notification/NotificationRepository.cs index 9e7aa56d9..e978bb544 100644 --- a/Proxytrace.Storage/Internal/Entities/Notification/NotificationRepository.cs +++ b/Proxytrace.Storage/Internal/Entities/Notification/NotificationRepository.cs @@ -11,6 +11,9 @@ internal class NotificationRepository : AbstractRepository, INotificationRepository { + /// + /// Initializes a new instance of the class. + /// public NotificationRepository( IMapper mapper, Func contextFactory, @@ -20,6 +23,9 @@ public NotificationRepository( { } + /// + /// Gets the for scope asynchronously. + /// public async Task> GetForScopeAsync( Guid? projectId, bool includeRead, @@ -41,6 +47,9 @@ public async Task> GetForScopeAsync( return await Map(stored, cancellationToken); } + /// + /// Counts the unread asynchronously. + /// public async Task CountUnreadAsync( Guid? projectId, CancellationToken cancellationToken = default) @@ -51,6 +60,9 @@ public async Task CountUnreadAsync( .Where(e => e.ProjectId == null || e.ProjectId == projectId) .CountAsync(cancellationToken); + /// + /// Finds the active by target asynchronously. + /// public async Task FindActiveByTargetAsync( NotificationTargetKind targetKind, Guid targetId, diff --git a/Proxytrace.Storage/Internal/Entities/OptimizationProposal/OptimizationProposalConfig.cs b/Proxytrace.Storage/Internal/Entities/OptimizationProposal/OptimizationProposalConfig.cs index 9ecd98e02..5a76932f0 100644 --- a/Proxytrace.Storage/Internal/Entities/OptimizationProposal/OptimizationProposalConfig.cs +++ b/Proxytrace.Storage/Internal/Entities/OptimizationProposal/OptimizationProposalConfig.cs @@ -26,6 +26,9 @@ internal class OptimizationProposalConfig : private readonly IRepository endpoints; private readonly IRepository testRuns; + /// + /// Initializes a new instance of the class. + /// public OptimizationProposalConfig( IModelSwitchProposal.CreateExisting createModelSwitch, ISystemPromptProposal.CreateExisting createSystemPrompt, @@ -44,6 +47,9 @@ public OptimizationProposalConfig( this.testRuns = testRuns; } + /// + /// Configures the application request pipeline. + /// public override void Configure(EntityTypeBuilder builder) { builder @@ -77,6 +83,9 @@ public override void Configure(EntityTypeBuilder bui builder.HasIndex(e => new { e.Agent, e.ContentHash }); } + /// + /// Maps. + /// public async Task Map(OptimizationProposalEntity stored, CancellationToken cancellationToken = default) { var agent = await agents.GetAsync(stored.Agent, cancellationToken); @@ -175,6 +184,9 @@ private IToolUpdateProposal MapToolUpdate( existing: stored); } + /// + /// Maps. + /// public Task Map(IOptimizationProposal domain, CancellationToken cancellationToken = default) { string data = domain switch diff --git a/Proxytrace.Storage/Internal/Entities/OptimizationProposal/OptimizationProposalRepository.cs b/Proxytrace.Storage/Internal/Entities/OptimizationProposal/OptimizationProposalRepository.cs index db54f446d..aab32cec6 100644 --- a/Proxytrace.Storage/Internal/Entities/OptimizationProposal/OptimizationProposalRepository.cs +++ b/Proxytrace.Storage/Internal/Entities/OptimizationProposal/OptimizationProposalRepository.cs @@ -12,6 +12,9 @@ internal class OptimizationProposalRepository : AbstractRepository, IOptimizationProposalRepository { + /// + /// Initializes a new instance of the class. + /// public OptimizationProposalRepository( IMapper mapper, Func contextFactory, @@ -21,6 +24,9 @@ public OptimizationProposalRepository( { } + /// + /// Gets the by agent asynchronously. + /// public async Task> GetByAgentAsync( Guid agentId, CancellationToken cancellationToken = default) @@ -35,11 +41,17 @@ public async Task> GetByAgentAsync( return await Map(stored, cancellationToken); } + /// + /// Gets the by project asynchronously. + /// public Task> GetByProjectAsync( Guid projectId, CancellationToken cancellationToken = default) => GetByProjectsAsync([projectId], cancellationToken); + /// + /// Gets the by projects asynchronously. + /// public async Task> GetByProjectsAsync( IReadOnlyCollection projectIds, CancellationToken cancellationToken = default) @@ -60,6 +72,9 @@ public async Task> GetByProjectsAsync( return await Map(stored, cancellationToken); } + /// + /// Finds the latest by content hash asynchronously. + /// public async Task FindLatestByContentHashAsync( Guid agentId, string contentHash, @@ -75,6 +90,9 @@ public async Task> GetByProjectsAsync( return await Map(stored, cancellationToken); } + /// + /// Gets the by agent and status asynchronously. + /// public async Task> GetByAgentAndStatusAsync( Guid agentId, ProposalStatus status, @@ -90,6 +108,9 @@ public async Task> GetByAgentAndStatusAsync return await Map(stored, cancellationToken); } + /// + /// Gets the by status asynchronously. + /// public async Task> GetByStatusAsync( ProposalStatus status, CancellationToken cancellationToken = default) diff --git a/Proxytrace.Storage/Internal/Entities/OptimizationTheory/OptimizationTheoryConfig.cs b/Proxytrace.Storage/Internal/Entities/OptimizationTheory/OptimizationTheoryConfig.cs index 4dce0525f..d6d07a60c 100644 --- a/Proxytrace.Storage/Internal/Entities/OptimizationTheory/OptimizationTheoryConfig.cs +++ b/Proxytrace.Storage/Internal/Entities/OptimizationTheory/OptimizationTheoryConfig.cs @@ -28,6 +28,9 @@ internal class OptimizationTheoryConfig : private readonly IRepository suites; private readonly IRepository endpoints; + /// + /// Initializes a new instance of the class. + /// public OptimizationTheoryConfig( IModelSwitchTheory.CreateExisting createModelSwitch, ISystemPromptTheory.CreateExisting createSystemPrompt, @@ -46,6 +49,9 @@ public OptimizationTheoryConfig( this.endpoints = endpoints; } + /// + /// Configures the application request pipeline. + /// public override void Configure(EntityTypeBuilder builder) { builder @@ -82,6 +88,9 @@ public override void Configure(EntityTypeBuilder build builder.HasIndex(e => new { e.Agent, e.ContentHash }); } + /// + /// Maps. + /// public async Task Map(OptimizationTheoryEntity stored, CancellationToken cancellationToken = default) { var agent = await agents.GetAsync(stored.Agent, cancellationToken); @@ -178,6 +187,9 @@ private IToolUpdateTheory MapToolUpdate( existing: stored); } + /// + /// Maps. + /// public Task Map(IOptimizationTheory domain, CancellationToken cancellationToken = default) { string data = domain switch diff --git a/Proxytrace.Storage/Internal/Entities/OptimizationTheory/OptimizationTheoryRepository.cs b/Proxytrace.Storage/Internal/Entities/OptimizationTheory/OptimizationTheoryRepository.cs index d23d36940..f0a14857d 100644 --- a/Proxytrace.Storage/Internal/Entities/OptimizationTheory/OptimizationTheoryRepository.cs +++ b/Proxytrace.Storage/Internal/Entities/OptimizationTheory/OptimizationTheoryRepository.cs @@ -12,6 +12,9 @@ internal class OptimizationTheoryRepository : AbstractRepository, IOptimizationTheoryRepository { + /// + /// Initializes a new instance of the class. + /// public OptimizationTheoryRepository( IMapper mapper, Func contextFactory, @@ -21,6 +24,9 @@ public OptimizationTheoryRepository( { } + /// + /// Gets the by agent asynchronously. + /// public async Task> GetByAgentAsync( Guid agentId, CancellationToken cancellationToken = default) @@ -35,6 +41,9 @@ public async Task> GetByAgentAsync( return await Map(stored, cancellationToken); } + /// + /// Gets the by project asynchronously. + /// public async Task> GetByProjectAsync( Guid projectId, CancellationToken cancellationToken = default) @@ -55,6 +64,9 @@ public async Task> GetByProjectAsync( return await Map(stored, cancellationToken); } + /// + /// Finds the latest by content hash asynchronously. + /// public async Task FindLatestByContentHashAsync( Guid agentId, string contentHash, @@ -70,6 +82,9 @@ public async Task> GetByProjectAsync( return await Map(stored, cancellationToken); } + /// + /// Counts the by project and status asynchronously. + /// public Task CountByProjectAndStatusAsync( Guid projectId, TheoryStatus status, @@ -87,6 +102,9 @@ public Task CountByProjectAndStatusAsync( .CountAsync(cancellationToken); } + /// + /// Gets the active asynchronously. + /// public async Task> GetActiveAsync( CancellationToken cancellationToken = default) { @@ -100,6 +118,9 @@ public async Task> GetActiveAsync( return await Map(stored, cancellationToken); } + /// + /// Counts the active by project asynchronously. + /// public Task CountActiveByProjectAsync( Guid projectId, CancellationToken cancellationToken = default) diff --git a/Proxytrace.Storage/Internal/Entities/OutlierSettings/OutlierSettingsConfig.cs b/Proxytrace.Storage/Internal/Entities/OutlierSettings/OutlierSettingsConfig.cs index 929abb0d1..133646992 100644 --- a/Proxytrace.Storage/Internal/Entities/OutlierSettings/OutlierSettingsConfig.cs +++ b/Proxytrace.Storage/Internal/Entities/OutlierSettings/OutlierSettingsConfig.cs @@ -4,6 +4,9 @@ namespace Proxytrace.Storage.Internal.Entities.OutlierSettings; internal class OutlierSettingsConfig : AbstractEntityConfiguration { + /// + /// Configures the application request pipeline. + /// public override void Configure(EntityTypeBuilder builder) { } diff --git a/Proxytrace.Storage/Internal/Entities/OutlierSettings/OutlierSettingsEntity.cs b/Proxytrace.Storage/Internal/Entities/OutlierSettings/OutlierSettingsEntity.cs index b91b7d198..ca43fbbae 100644 --- a/Proxytrace.Storage/Internal/Entities/OutlierSettings/OutlierSettingsEntity.cs +++ b/Proxytrace.Storage/Internal/Entities/OutlierSettings/OutlierSettingsEntity.cs @@ -3,8 +3,20 @@ namespace Proxytrace.Storage.Internal.Entities.OutlierSettings; /// The single-row outlier-detection sensitivity configuration. internal record OutlierSettingsEntity : Entity { + /// + /// Gets or sets the enabled. + /// public required bool Enabled { get; init; } + /// + /// Gets or sets the sigma multiplier. + /// public required double SigmaMultiplier { get; init; } + /// + /// Gets or sets the min sample count. + /// public required int MinSampleCount { get; init; } + /// + /// Gets or sets the sample window. + /// public required int SampleWindow { get; init; } } diff --git a/Proxytrace.Storage/Internal/Entities/OutlierSettings/OutlierSettingsStore.cs b/Proxytrace.Storage/Internal/Entities/OutlierSettings/OutlierSettingsStore.cs index 67a5e2a60..23b60b1d1 100644 --- a/Proxytrace.Storage/Internal/Entities/OutlierSettings/OutlierSettingsStore.cs +++ b/Proxytrace.Storage/Internal/Entities/OutlierSettings/OutlierSettingsStore.cs @@ -12,12 +12,18 @@ internal sealed class OutlierSettingsStore : IOutlierSettingsStore private readonly Func contextFactory; private readonly IClock clock; + /// + /// Initializes a new instance of the class. + /// public OutlierSettingsStore(Func contextFactory, IClock clock) { this.contextFactory = contextFactory; this.clock = clock; } + /// + /// Gets asynchronously. + /// public async Task GetAsync(CancellationToken cancellationToken = default) { OutlierSettingsEntity? entity = await contextFactory() @@ -33,6 +39,9 @@ public OutlierSettingsStore(Func contextFactory, IClock clock) entity.Enabled, entity.SigmaMultiplier, entity.MinSampleCount, entity.SampleWindow); } + /// + /// Saves asynchronously. + /// public async Task SaveAsync(AppOutlierSettings settings, CancellationToken cancellationToken = default) { StorageDbContext context = contextFactory(); diff --git a/Proxytrace.Storage/Internal/Entities/PasswordResetToken/PasswordResetTokenConfig.cs b/Proxytrace.Storage/Internal/Entities/PasswordResetToken/PasswordResetTokenConfig.cs index 9a3396c25..93cd44713 100644 --- a/Proxytrace.Storage/Internal/Entities/PasswordResetToken/PasswordResetTokenConfig.cs +++ b/Proxytrace.Storage/Internal/Entities/PasswordResetToken/PasswordResetTokenConfig.cs @@ -15,12 +15,18 @@ internal class PasswordResetTokenConfig private readonly IPasswordResetToken.CreateExisting factory; private readonly IRepository users; + /// + /// Initializes a new instance of the class. + /// public PasswordResetTokenConfig(IPasswordResetToken.CreateExisting factory, IRepository users) { this.factory = factory; this.users = users; } + /// + /// Configures the application request pipeline. + /// public override void Configure(EntityTypeBuilder builder) { builder.HasIndex(e => e.TokenHash).IsUnique(); @@ -32,12 +38,18 @@ public override void Configure(EntityTypeBuilder build .OnDelete(DeleteBehavior.Cascade); } + /// + /// Maps. + /// public async Task Map(PasswordResetTokenEntity stored, CancellationToken cancellationToken = default) { var user = await users.GetAsync(stored.User, cancellationToken); return factory(user, stored.TokenHash, stored.ExpiresAt, stored.ConsumedAt, stored); } + /// + /// Maps. + /// public Task Map(IPasswordResetToken domain, CancellationToken cancellationToken = default) => new PasswordResetTokenEntity { diff --git a/Proxytrace.Storage/Internal/Entities/PasswordResetToken/PasswordResetTokenEntity.cs b/Proxytrace.Storage/Internal/Entities/PasswordResetToken/PasswordResetTokenEntity.cs index 5d36ec4c5..44bd49296 100644 --- a/Proxytrace.Storage/Internal/Entities/PasswordResetToken/PasswordResetTokenEntity.cs +++ b/Proxytrace.Storage/Internal/Entities/PasswordResetToken/PasswordResetTokenEntity.cs @@ -5,6 +5,9 @@ namespace Proxytrace.Storage.Internal.Entities.PasswordResetToken; [StoredDomainEntity(typeof(IPasswordResetToken))] internal record PasswordResetTokenEntity : Entity { + /// + /// Gets or sets the user. + /// public required Guid User { get; init; } /// @@ -12,6 +15,12 @@ internal record PasswordResetTokenEntity : Entity /// its hash is stored). /// public required string TokenHash { get; init; } + /// + /// Gets or sets the expires at. + /// public required DateTimeOffset ExpiresAt { get; init; } + /// + /// Gets or sets the consumed at. + /// public DateTimeOffset? ConsumedAt { get; init; } } diff --git a/Proxytrace.Storage/Internal/Entities/PasswordResetToken/PasswordResetTokenRepository.cs b/Proxytrace.Storage/Internal/Entities/PasswordResetToken/PasswordResetTokenRepository.cs index 3e0884c9b..cf721c7da 100644 --- a/Proxytrace.Storage/Internal/Entities/PasswordResetToken/PasswordResetTokenRepository.cs +++ b/Proxytrace.Storage/Internal/Entities/PasswordResetToken/PasswordResetTokenRepository.cs @@ -14,6 +14,9 @@ internal class PasswordResetTokenRepository { private readonly ISecretHasher hasher; + /// + /// Initializes a new instance of the class. + /// public PasswordResetTokenRepository( IMapper mapper, Func context, @@ -25,6 +28,9 @@ public PasswordResetTokenRepository( this.hasher = hasher; } + /// + /// Finds the by token asynchronously. + /// public async Task FindByTokenAsync(string token, CancellationToken cancellationToken = default) { // The token is stored as a hash; match on the hash of the presented raw token. diff --git a/Proxytrace.Storage/Internal/Entities/Project/ProjectConfig.cs b/Proxytrace.Storage/Internal/Entities/Project/ProjectConfig.cs index b9fb32ff2..d84903f27 100644 --- a/Proxytrace.Storage/Internal/Entities/Project/ProjectConfig.cs +++ b/Proxytrace.Storage/Internal/Entities/Project/ProjectConfig.cs @@ -16,6 +16,9 @@ internal class ProjectConfig : AbstractEntityConfiguration, IMapp private readonly IRepository users; private readonly Func contextFactory; + /// + /// Initializes a new instance of the class. + /// public ProjectConfig( IProject.CreateExisting factory, IRepository endpoints, @@ -28,6 +31,9 @@ public ProjectConfig( this.contextFactory = contextFactory; } + /// + /// Configures the application request pipeline. + /// public override void Configure(EntityTypeBuilder builder) { builder.HasIndex(e => e.Name).IsUnique(); @@ -39,6 +45,9 @@ public override void Configure(EntityTypeBuilder builder) .OnDelete(DeleteBehavior.Restrict); } + /// + /// Maps. + /// public async Task Map(ProjectEntity stored, CancellationToken cancellationToken = default) { var endpoint = await endpoints.GetAsync(stored.SystemEndpoint, cancellationToken); @@ -57,6 +66,9 @@ public async Task Map(ProjectEntity stored, CancellationToken cancella return factory(stored.Name, endpoint, members, stored); } + /// + /// Maps. + /// public Task Map(IProject domain, CancellationToken cancellationToken = default) => new ProjectEntity { diff --git a/Proxytrace.Storage/Internal/Entities/Project/ProjectRepository.cs b/Proxytrace.Storage/Internal/Entities/Project/ProjectRepository.cs index fc803535b..8a2592d23 100644 --- a/Proxytrace.Storage/Internal/Entities/Project/ProjectRepository.cs +++ b/Proxytrace.Storage/Internal/Entities/Project/ProjectRepository.cs @@ -14,6 +14,9 @@ internal class ProjectRepository : AbstractRepository, { private readonly ILogger logger; + /// + /// Initializes a new instance of the class. + /// public ProjectRepository( IMapper mapper, Func contextFactory, @@ -25,6 +28,9 @@ public ProjectRepository( this.logger = logger; } + /// + /// Finds the by name asynchronously. + /// public async Task FindByNameAsync( string name, CancellationToken cancellationToken = default) @@ -36,6 +42,9 @@ public ProjectRepository( return await Map(entity, cancellationToken); } + /// + /// Finds the by slug asynchronously. + /// public async Task FindBySlugAsync( string slug, CancellationToken cancellationToken = default) @@ -78,6 +87,9 @@ public ProjectRepository( return await this.GetAsync(matches[0].Id, cancellationToken); } + /// + /// Gets the by member asynchronously. + /// public async Task> GetByMemberAsync( Guid userId, CancellationToken cancellationToken = default) diff --git a/Proxytrace.Storage/Internal/Entities/Project/ProjectUserConfig.cs b/Proxytrace.Storage/Internal/Entities/Project/ProjectUserConfig.cs index 1ff409c88..4238a0c88 100644 --- a/Proxytrace.Storage/Internal/Entities/Project/ProjectUserConfig.cs +++ b/Proxytrace.Storage/Internal/Entities/Project/ProjectUserConfig.cs @@ -6,6 +6,9 @@ namespace Proxytrace.Storage.Internal.Entities.Project; internal class ProjectUserConfig : AbstractEntityConfiguration { + /// + /// Configures the application request pipeline. + /// public override void Configure(EntityTypeBuilder builder) { builder.HasKey(e => new { e.ProjectId, e.UserId }); diff --git a/Proxytrace.Storage/Internal/Entities/Project/ProjectUserEntity.cs b/Proxytrace.Storage/Internal/Entities/Project/ProjectUserEntity.cs index aea412bcd..593580a43 100644 --- a/Proxytrace.Storage/Internal/Entities/Project/ProjectUserEntity.cs +++ b/Proxytrace.Storage/Internal/Entities/Project/ProjectUserEntity.cs @@ -6,6 +6,12 @@ namespace Proxytrace.Storage.Internal.Entities.Project; /// internal record ProjectUserEntity { + /// + /// Gets or sets the project id. + /// public required Guid ProjectId { get; init; } + /// + /// Gets or sets the user id. + /// public required Guid UserId { get; init; } } diff --git a/Proxytrace.Storage/Internal/Entities/ProjectSearchSettings/ProjectSearchSettingsConfig.cs b/Proxytrace.Storage/Internal/Entities/ProjectSearchSettings/ProjectSearchSettingsConfig.cs index 8379073b4..ddf9499fd 100644 --- a/Proxytrace.Storage/Internal/Entities/ProjectSearchSettings/ProjectSearchSettingsConfig.cs +++ b/Proxytrace.Storage/Internal/Entities/ProjectSearchSettings/ProjectSearchSettingsConfig.cs @@ -16,6 +16,9 @@ internal class ProjectSearchSettingsConfig private readonly IProjectSearchSettings.CreateExisting factory; private readonly IRepository projects; + /// + /// Initializes a new instance of the class. + /// public ProjectSearchSettingsConfig( IProjectSearchSettings.CreateExisting factory, IRepository projects) @@ -24,6 +27,9 @@ public ProjectSearchSettingsConfig( this.projects = projects; } + /// + /// Configures the application request pipeline. + /// public override void Configure(EntityTypeBuilder builder) { builder.HasIndex(e => e.Project).IsUnique(); @@ -37,6 +43,9 @@ public override void Configure(EntityTypeBuilder bu builder.Property(e => e.IndexedKinds).HasMaxLength(256); } + /// + /// Maps. + /// public async Task Map(ProjectSearchSettingsEntity stored, CancellationToken cancellationToken = default) { var project = await projects.GetAsync(stored.Project, cancellationToken); @@ -50,6 +59,9 @@ public async Task Map(ProjectSearchSettingsEntity stored existing: stored); } + /// + /// Maps. + /// public Task Map(IProjectSearchSettings domain, CancellationToken cancellationToken = default) => new ProjectSearchSettingsEntity { diff --git a/Proxytrace.Storage/Internal/Entities/ProjectSearchSettings/ProjectSearchSettingsRepository.cs b/Proxytrace.Storage/Internal/Entities/ProjectSearchSettings/ProjectSearchSettingsRepository.cs index 75d811065..d86a92bda 100644 --- a/Proxytrace.Storage/Internal/Entities/ProjectSearchSettings/ProjectSearchSettingsRepository.cs +++ b/Proxytrace.Storage/Internal/Entities/ProjectSearchSettings/ProjectSearchSettingsRepository.cs @@ -11,6 +11,9 @@ internal class ProjectSearchSettingsRepository : AbstractRepository, IProjectSearchSettingsRepository { + /// + /// Initializes a new instance of the class. + /// public ProjectSearchSettingsRepository( IMapper mapper, Func contextFactory, @@ -20,6 +23,9 @@ public ProjectSearchSettingsRepository( { } + /// + /// Finds the by project asynchronously. + /// public async Task FindByProjectAsync(Guid projectId, CancellationToken cancellationToken = default) { var stored = await contextFactory() diff --git a/Proxytrace.Storage/Internal/Entities/Session/SessionConfig.cs b/Proxytrace.Storage/Internal/Entities/Session/SessionConfig.cs index 4ae1fc2bf..a0d897836 100644 --- a/Proxytrace.Storage/Internal/Entities/Session/SessionConfig.cs +++ b/Proxytrace.Storage/Internal/Entities/Session/SessionConfig.cs @@ -12,11 +12,17 @@ internal class SessionConfig { private readonly ISession.CreateExisting factory; + /// + /// Initializes a new instance of the class. + /// public SessionConfig(ISession.CreateExisting factory) { this.factory = factory; } + /// + /// Configures the application request pipeline. + /// public override void Configure(EntityTypeBuilder builder) { // Sessions are project-owned debugging groupings; they go away with their project. The @@ -33,6 +39,9 @@ public override void Configure(EntityTypeBuilder builder) builder.HasIndex(e => new { e.ProjectId, e.LastActivityAt }).IsDescending(false, true); } + /// + /// Maps. + /// public Task Map(SessionEntity storedEntity, CancellationToken cancellationToken = default) => factory( externalKey: storedEntity.ExternalKey, @@ -42,6 +51,9 @@ public Task Map(SessionEntity storedEntity, CancellationToken cancella totalTokens: storedEntity.TotalTokens, existing: storedEntity).ToTaskResult(); + /// + /// Maps. + /// public Task Map(ISession domainEntity, CancellationToken cancellationToken = default) => new SessionEntity { diff --git a/Proxytrace.Storage/Internal/Entities/Session/SessionEntity.cs b/Proxytrace.Storage/Internal/Entities/Session/SessionEntity.cs index edd74f935..eb56d6849 100644 --- a/Proxytrace.Storage/Internal/Entities/Session/SessionEntity.cs +++ b/Proxytrace.Storage/Internal/Entities/Session/SessionEntity.cs @@ -5,9 +5,24 @@ namespace Proxytrace.Storage.Internal.Entities.Session; [StoredDomainEntity(typeof(ISession))] internal record SessionEntity : Entity { + /// + /// Gets or sets the external key. + /// public required string ExternalKey { get; init; } + /// + /// Gets or sets the project id. + /// public required Guid ProjectId { get; init; } + /// + /// Gets or sets the last activity at. + /// public required DateTimeOffset LastActivityAt { get; init; } + /// + /// Gets or sets the trace count. + /// public required int TraceCount { get; init; } + /// + /// Gets or sets the total tokens. + /// public required long TotalTokens { get; init; } } diff --git a/Proxytrace.Storage/Internal/Entities/Session/SessionRepository.cs b/Proxytrace.Storage/Internal/Entities/Session/SessionRepository.cs index 4697e2846..8a425a0f9 100644 --- a/Proxytrace.Storage/Internal/Entities/Session/SessionRepository.cs +++ b/Proxytrace.Storage/Internal/Entities/Session/SessionRepository.cs @@ -15,6 +15,9 @@ internal class SessionRepository { private readonly ILogger logger; + /// + /// Initializes a new instance of the class. + /// public SessionRepository( IMapper mapper, Func contextFactory, @@ -26,6 +29,9 @@ public SessionRepository( this.logger = logger; } + /// + /// Record activity asynchronously. + /// public async Task RecordActivityAsync( Guid sessionId, string externalKey, @@ -86,6 +92,9 @@ public async Task RecordActivityAsync( await context.SaveChangesAsync(cancellationToken); } + /// + /// Record trace removals asynchronously. + /// public async Task RecordTraceRemovalsAsync( IReadOnlyCollection removals, CancellationToken cancellationToken = default) @@ -128,6 +137,9 @@ await context.Set() } } + /// + /// Removes the older than asynchronously. + /// public async Task RemoveOlderThanAsync(DateTimeOffset cutoff, CancellationToken cancellationToken = default) { var context = contextFactory(); @@ -177,6 +189,9 @@ private static SessionEntity NewRow( UpdatedAt = lastActivityAt, }; + /// + /// Gets the recent asynchronously. + /// public async Task<(IReadOnlyList Items, int Total)> GetRecentAsync( Guid projectId, int page, diff --git a/Proxytrace.Storage/Internal/Entities/Statistics/TestRunStatsConfig.cs b/Proxytrace.Storage/Internal/Entities/Statistics/TestRunStatsConfig.cs index b99e85731..1bb8cd436 100644 --- a/Proxytrace.Storage/Internal/Entities/Statistics/TestRunStatsConfig.cs +++ b/Proxytrace.Storage/Internal/Entities/Statistics/TestRunStatsConfig.cs @@ -6,6 +6,9 @@ namespace Proxytrace.Storage.Internal.Entities.Statistics; internal class TestRunStatsConfig : AbstractEntityConfiguration { + /// + /// Configures the application request pipeline. + /// public override void Configure(EntityTypeBuilder builder) { builder diff --git a/Proxytrace.Storage/Internal/Entities/Statistics/TestRunStatsEntity.cs b/Proxytrace.Storage/Internal/Entities/Statistics/TestRunStatsEntity.cs index 2d1f846da..e67a56c8a 100644 --- a/Proxytrace.Storage/Internal/Entities/Statistics/TestRunStatsEntity.cs +++ b/Proxytrace.Storage/Internal/Entities/Statistics/TestRunStatsEntity.cs @@ -2,17 +2,56 @@ namespace Proxytrace.Storage.Internal.Entities.Statistics; internal record TestRunStatsEntity : Entity { + /// + /// Gets or sets the test run id. + /// public required Guid TestRunId { get; init; } + /// + /// Gets or sets the agent id. + /// public required Guid AgentId { get; init; } + /// + /// Gets or sets the endpoint id. + /// public required Guid EndpointId { get; init; } + /// + /// Gets or sets the group id. + /// public required Guid GroupId { get; init; } + /// + /// Gets or sets the suite id. + /// public required Guid SuiteId { get; init; } + /// + /// Gets or sets the test cases. + /// public required int TestCases { get; init; } + /// + /// Gets or sets the passed. + /// public required int Passed { get; init; } + /// + /// Gets or sets the input tokens. + /// public long? InputTokens { get; init; } + /// + /// Gets or sets the output tokens. + /// public long? OutputTokens { get; init; } + /// + /// Gets or sets the cached input tokens. + /// public long? CachedInputTokens { get; init; } + /// + /// Gets or sets the total duration microseconds. + /// public long? TotalDurationMicroseconds { get; init; } + /// + /// Gets or sets the cost. + /// public decimal? Cost { get; init; } + /// + /// Gets or sets the run completed at. + /// public required DateTimeOffset RunCompletedAt { get; init; } } diff --git a/Proxytrace.Storage/Internal/Entities/TestCase/TestCaseConfig.cs b/Proxytrace.Storage/Internal/Entities/TestCase/TestCaseConfig.cs index 0684256c8..3ca52931c 100644 --- a/Proxytrace.Storage/Internal/Entities/TestCase/TestCaseConfig.cs +++ b/Proxytrace.Storage/Internal/Entities/TestCase/TestCaseConfig.cs @@ -11,12 +11,18 @@ internal class TestCaseConfig : AbstractEntityConfiguration, IMa private readonly ITestCase.CreateExisting factory; private readonly ISerializer serializer; + /// + /// Initializes a new instance of the class. + /// public TestCaseConfig(ITestCase.CreateExisting factory, ISerializer serializer) { this.factory = factory; this.serializer = serializer; } + /// + /// Configures the application request pipeline. + /// public override void Configure(EntityTypeBuilder builder) { builder @@ -34,9 +40,15 @@ public override void Configure(EntityTypeBuilder builder) ); } + /// + /// Maps. + /// public Task Map(TestCaseEntity stored, CancellationToken cancellationToken = default) => factory(stored.Input, stored.ExpectedOutput, stored.SourceAgentCallId, stored).ToTaskResult(); + /// + /// Maps. + /// public Task Map(ITestCase domain, CancellationToken cancellationToken = default) => new TestCaseEntity { diff --git a/Proxytrace.Storage/Internal/Entities/TestCase/TestCaseRepository.cs b/Proxytrace.Storage/Internal/Entities/TestCase/TestCaseRepository.cs index e84accc5f..676451f09 100644 --- a/Proxytrace.Storage/Internal/Entities/TestCase/TestCaseRepository.cs +++ b/Proxytrace.Storage/Internal/Entities/TestCase/TestCaseRepository.cs @@ -8,6 +8,9 @@ namespace Proxytrace.Storage.Internal.Entities.TestCase; [UsedImplicitly] internal class TestCaseRepository : AbstractRepository, ITestCaseRepository { + /// + /// Initializes a new instance of the class. + /// public TestCaseRepository( IMapper mapper, Func contextFactory, diff --git a/Proxytrace.Storage/Internal/Entities/TestResult/EvaluationStatConfig.cs b/Proxytrace.Storage/Internal/Entities/TestResult/EvaluationStatConfig.cs index d7f17af0e..b810e7ed3 100644 --- a/Proxytrace.Storage/Internal/Entities/TestResult/EvaluationStatConfig.cs +++ b/Proxytrace.Storage/Internal/Entities/TestResult/EvaluationStatConfig.cs @@ -5,6 +5,9 @@ namespace Proxytrace.Storage.Internal.Entities.TestResult; internal class EvaluationStatConfig : AbstractEntityConfiguration { + /// + /// Configures the application request pipeline. + /// public override void Configure(EntityTypeBuilder builder) { builder.HasKey(e => e.Id); diff --git a/Proxytrace.Storage/Internal/Entities/TestResult/EvaluationStatEntity.cs b/Proxytrace.Storage/Internal/Entities/TestResult/EvaluationStatEntity.cs index 6027a263e..8bb2cde12 100644 --- a/Proxytrace.Storage/Internal/Entities/TestResult/EvaluationStatEntity.cs +++ b/Proxytrace.Storage/Internal/Entities/TestResult/EvaluationStatEntity.cs @@ -18,11 +18,17 @@ namespace Proxytrace.Storage.Internal.Entities.TestResult; /// internal record EvaluationStatEntity { + /// + /// Gets or sets the id. + /// public required Guid Id { get; init; } /// The owning . public required Guid TestResultId { get; init; } + /// + /// Gets or sets the evaluator id. + /// public required Guid EvaluatorId { get; init; } /// @@ -31,15 +37,33 @@ internal record EvaluationStatEntity /// public required DateTimeOffset CreatedAt { get; init; } + /// + /// Gets or sets the score. + /// public EvaluationScore? Score { get; init; } /// True for an errored evaluation (no score); mirrors a non-null /// StoredEvaluation.ErrorMessage. The error text itself is not projected. public bool HasError { get; init; } + /// + /// Gets or sets the input tokens. + /// public long? InputTokens { get; init; } + /// + /// Gets or sets the output tokens. + /// public long? OutputTokens { get; init; } + /// + /// Gets or sets the cached input tokens. + /// public long? CachedInputTokens { get; init; } + /// + /// Gets or sets the latency microseconds. + /// public long LatencyMicroseconds { get; init; } + /// + /// Gets or sets the cost. + /// public decimal? Cost { get; init; } } diff --git a/Proxytrace.Storage/Internal/Entities/TestResult/TestResultConfig.cs b/Proxytrace.Storage/Internal/Entities/TestResult/TestResultConfig.cs index 320d19730..69c1f4df6 100644 --- a/Proxytrace.Storage/Internal/Entities/TestResult/TestResultConfig.cs +++ b/Proxytrace.Storage/Internal/Entities/TestResult/TestResultConfig.cs @@ -23,6 +23,9 @@ internal class TestResultConfig : AbstractEntityConfiguration, private readonly IEvaluation.CreateErrored createErroredEvaluation; private readonly ISerializer serializer; + /// + /// Initializes a new instance of the class. + /// public TestResultConfig( IRepository testCases, IRepository evaluators, @@ -39,6 +42,9 @@ public TestResultConfig( this.serializer = serializer; } + /// + /// Configures the application request pipeline. + /// public override void Configure(EntityTypeBuilder builder) { // The evaluator-history queries scan the most recent N results ordered by CreatedAt; @@ -66,6 +72,9 @@ public override void Configure(EntityTypeBuilder builder) ); } + /// + /// Maps. + /// public async Task Map(TestResultEntity stored, CancellationToken cancellationToken = default) { var evaluations = new List(); @@ -100,6 +109,9 @@ public async Task Map(TestResultEntity stored, CancellationToken ca existing: stored); } + /// + /// Maps. + /// public Task Map(ITestResult domain, CancellationToken cancellationToken = default) => new TestResultEntity { diff --git a/Proxytrace.Storage/Internal/Entities/TestResult/TestResultEntity.cs b/Proxytrace.Storage/Internal/Entities/TestResult/TestResultEntity.cs index 3b4585391..a1e6851c9 100644 --- a/Proxytrace.Storage/Internal/Entities/TestResult/TestResultEntity.cs +++ b/Proxytrace.Storage/Internal/Entities/TestResult/TestResultEntity.cs @@ -7,12 +7,33 @@ namespace Proxytrace.Storage.Internal.Entities.TestResult; [StoredDomainEntity(typeof(ITestResult))] internal record TestResultEntity : Entity { + /// + /// Gets or sets the test case. + /// public required Guid TestCase { get; init; } + /// + /// Gets or sets the actual response. + /// public required AssistantMessage ActualResponse { get; init; } + /// + /// Gets or sets the evaluations. + /// public required IReadOnlyCollection Evaluations { get; init; } + /// + /// Gets or sets the duration ms. + /// public required long DurationMs { get; init; } + /// + /// Gets or sets the input tokens. + /// public required long? InputTokens { get; init; } + /// + /// Gets or sets the output tokens. + /// public required long? OutputTokens { get; init; } + /// + /// Gets or sets the cached input tokens. + /// public required long? CachedInputTokens { get; init; } /// @@ -29,13 +50,40 @@ internal record TestResultEntity : Entity /// internal record StoredEvaluation { + /// + /// Gets or sets the evaluator id. + /// public required Guid EvaluatorId { get; init; } + /// + /// Gets or sets the score. + /// public EvaluationScore? Score { get; init; } + /// + /// Gets or sets the reasoning. + /// public string? Reasoning { get; init; } + /// + /// Gets or sets the error message. + /// public string? ErrorMessage { get; init; } + /// + /// Gets or sets the input tokens. + /// public long? InputTokens { get; init; } + /// + /// Gets or sets the output tokens. + /// public long? OutputTokens { get; init; } + /// + /// Gets or sets the cached input tokens. + /// public long? CachedInputTokens { get; init; } + /// + /// Gets or sets the latency microseconds. + /// public long LatencyMicroseconds { get; init; } + /// + /// Gets or sets the cost. + /// public decimal? Cost { get; init; } } diff --git a/Proxytrace.Storage/Internal/Entities/TestResult/TestResultRepository.cs b/Proxytrace.Storage/Internal/Entities/TestResult/TestResultRepository.cs index 017b1c3e2..a7615ace0 100644 --- a/Proxytrace.Storage/Internal/Entities/TestResult/TestResultRepository.cs +++ b/Proxytrace.Storage/Internal/Entities/TestResult/TestResultRepository.cs @@ -23,6 +23,9 @@ internal class TestResultRepository : AbstractRepository Math.Min(MaxCandidateWindow, Math.Max(count, count * CandidateWindowPerResult)); + /// + /// Initializes a new instance of the class. + /// public TestResultRepository( IMapper mapper, Func contextFactory, @@ -49,6 +52,9 @@ protected override async Task UpdateRelationsAsync( context.Set().AddRange(storedEntity.EvaluationStats); } + /// + /// Gets the latest by test case asynchronously. + /// public async Task GetLatestByTestCaseAsync(Guid testCaseId, CancellationToken cancellationToken = default) { var context = contextFactory(); @@ -62,6 +68,9 @@ protected override async Task UpdateRelationsAsync( return await Map(stored, cancellationToken); } + /// + /// Gets the latest by evaluator asynchronously. + /// public async Task GetLatestByEvaluatorAsync(Guid evaluatorId, CancellationToken cancellationToken = default) { var context = contextFactory(); @@ -83,6 +92,9 @@ protected override async Task UpdateRelationsAsync( return await Map(entity, cancellationToken); } + /// + /// Gets the recent by evaluator asynchronously. + /// public async Task> GetRecentByEvaluatorAsync( Guid evaluatorId, int count, @@ -98,6 +110,9 @@ public async Task> GetRecentByEvaluatorAsync( return await LoadFullInOrderAsync(context, matchingIds, cancellationToken); } + /// + /// Searches the by evaluator asynchronously. + /// public async Task> SearchByEvaluatorAsync( Guid evaluatorId, string query, diff --git a/Proxytrace.Storage/Internal/Entities/TestRun/TestRunConfig.cs b/Proxytrace.Storage/Internal/Entities/TestRun/TestRunConfig.cs index 8cd05e1e8..d44e41da0 100644 --- a/Proxytrace.Storage/Internal/Entities/TestRun/TestRunConfig.cs +++ b/Proxytrace.Storage/Internal/Entities/TestRun/TestRunConfig.cs @@ -20,6 +20,9 @@ internal class TestRunConfig : AbstractEntityConfiguration, IMapp private readonly ITestRun.CreateExisting factory; private readonly ISerializer serializer; + /// + /// Initializes a new instance of the class. + /// public TestRunConfig( IRepository endpoints, IRepository testResults, @@ -34,6 +37,9 @@ public TestRunConfig( this.serializer = serializer; } + /// + /// Configures the application request pipeline. + /// public override void Configure(EntityTypeBuilder builder) { builder @@ -56,6 +62,9 @@ public override void Configure(EntityTypeBuilder builder) ); } + /// + /// Maps. + /// public async Task Map(TestRunEntity stored, CancellationToken cancellationToken = default) { // Sequential, not concurrent: these reads may share the ambient transaction context, @@ -74,6 +83,9 @@ public async Task Map(TestRunEntity stored, CancellationToken cancella existing: stored); } + /// + /// Maps. + /// public Task Map(ITestRun domain, CancellationToken cancellationToken = default) => new TestRunEntity { diff --git a/Proxytrace.Storage/Internal/Entities/TestRun/TestRunEntity.cs b/Proxytrace.Storage/Internal/Entities/TestRun/TestRunEntity.cs index eaeb49014..b9c7eef29 100644 --- a/Proxytrace.Storage/Internal/Entities/TestRun/TestRunEntity.cs +++ b/Proxytrace.Storage/Internal/Entities/TestRun/TestRunEntity.cs @@ -5,10 +5,28 @@ namespace Proxytrace.Storage.Internal.Entities.TestRun; [StoredDomainEntity(typeof(ITestRun))] internal record TestRunEntity : Entity { + /// + /// Gets or sets the group. + /// public required Guid Group { get; init; } + /// + /// Gets or sets the endpoint. + /// public required Guid Endpoint { get; init; } + /// + /// Gets or sets the sample index. + /// public int SampleIndex { get; init; } + /// + /// Gets or sets the status. + /// public required TestRunStatus Status { get; init; } + /// + /// Gets or sets the completed at. + /// public DateTimeOffset? CompletedAt { get; init; } + /// + /// Gets or sets the test results. + /// public required IReadOnlyCollection TestResults { get; init; } } diff --git a/Proxytrace.Storage/Internal/Entities/TestRun/TestRunRepository.cs b/Proxytrace.Storage/Internal/Entities/TestRun/TestRunRepository.cs index 9c8aab653..a8d7bf7da 100644 --- a/Proxytrace.Storage/Internal/Entities/TestRun/TestRunRepository.cs +++ b/Proxytrace.Storage/Internal/Entities/TestRun/TestRunRepository.cs @@ -13,6 +13,9 @@ namespace Proxytrace.Storage.Internal.Entities.TestRun; [UsedImplicitly] internal class TestRunRepository : AbstractRepository, ITestRunRepository { + /// + /// Initializes a new instance of the class. + /// public TestRunRepository( IMapper mapper, Func contextFactory, @@ -22,6 +25,9 @@ public TestRunRepository( { } + /// + /// Gets the by agent asynchronously. + /// public async Task> GetByAgentAsync(Guid agentId, CancellationToken cancellationToken = default) { var context = contextFactory(); @@ -43,6 +49,9 @@ public async Task> GetByAgentAsync(Guid agentId, Cancell return await Map(stored, cancellationToken); } + /// + /// Gets the by agent paged asynchronously. + /// public async Task> GetByAgentPagedAsync( Guid agentId, int page, @@ -76,6 +85,9 @@ public async Task> GetByAgentPagedAsync( return new PagedResult(await Map(stored, cancellationToken), total, page, pageSize); } + /// + /// Gets the all paged asynchronously. + /// public async Task> GetAllPagedAsync( int page, int pageSize, @@ -104,6 +116,9 @@ public async Task> GetAllPagedAsync( return new PagedResult(await Map(stored, cancellationToken), total, page, pageSize); } + /// + /// Gets the by projects paged asynchronously. + /// public async Task> GetByProjectsPagedAsync( IReadOnlyCollection projectIds, int page, @@ -141,6 +156,9 @@ public async Task> GetByProjectsPagedAsync( return new PagedResult(await Map(stored, cancellationToken), total, page, pageSize); } + /// + /// Gets the by group asynchronously. + /// public async Task> GetByGroupAsync(Guid groupId, CancellationToken cancellationToken = default) { var context = contextFactory(); @@ -153,6 +171,9 @@ public async Task> GetByGroupAsync(Guid groupId, Cancell return await Map(stored, cancellationToken); } + /// + /// Gets the by status asynchronously. + /// public async Task> GetByStatusAsync( IReadOnlyCollection statuses, CancellationToken cancellationToken = default) @@ -168,6 +189,9 @@ public async Task> GetByStatusAsync( return await Map(stored, cancellationToken); } + /// + /// Gets the run ids by result ids asynchronously. + /// public async Task> GetRunIdsByResultIdsAsync( IReadOnlyCollection resultIds, CancellationToken cancellationToken = default) diff --git a/Proxytrace.Storage/Internal/Entities/TestRunGroup/TestRunGroupConfig.cs b/Proxytrace.Storage/Internal/Entities/TestRunGroup/TestRunGroupConfig.cs index 2e6c64edd..0dbfb42c2 100644 --- a/Proxytrace.Storage/Internal/Entities/TestRunGroup/TestRunGroupConfig.cs +++ b/Proxytrace.Storage/Internal/Entities/TestRunGroup/TestRunGroupConfig.cs @@ -14,6 +14,9 @@ internal class TestRunGroupConfig : AbstractEntityConfiguration suites; private readonly ITestRunGroup.CreateExisting factory; + /// + /// Initializes a new instance of the class. + /// public TestRunGroupConfig( IRepository suites, ITestRunGroup.CreateExisting factory) @@ -22,6 +25,9 @@ public TestRunGroupConfig( this.factory = factory; } + /// + /// Configures the application request pipeline. + /// public override void Configure(EntityTypeBuilder builder) { builder @@ -47,6 +53,9 @@ public override void Configure(EntityTypeBuilder builder) builder.HasIndex(e => new { e.OptimizationConsideredAt, e.IsSystemRun, e.Status }); } + /// + /// Maps. + /// public async Task Map(TestRunGroupEntity stored, CancellationToken cancellationToken = default) => factory( suite: await suites.GetAsync(stored.Suite, cancellationToken), @@ -58,6 +67,9 @@ public async Task Map(TestRunGroupEntity stored, CancellationToke optimizationConsideredAt: stored.OptimizationConsideredAt, existing: stored); + /// + /// Maps. + /// public Task Map(ITestRunGroup domain, CancellationToken cancellationToken = default) => new TestRunGroupEntity { diff --git a/Proxytrace.Storage/Internal/Entities/TestRunGroup/TestRunGroupEntity.cs b/Proxytrace.Storage/Internal/Entities/TestRunGroup/TestRunGroupEntity.cs index 12b29be14..49c65f958 100644 --- a/Proxytrace.Storage/Internal/Entities/TestRunGroup/TestRunGroupEntity.cs +++ b/Proxytrace.Storage/Internal/Entities/TestRunGroup/TestRunGroupEntity.cs @@ -6,11 +6,29 @@ namespace Proxytrace.Storage.Internal.Entities.TestRunGroup; [StoredDomainEntity(typeof(ITestRunGroup))] internal record TestRunGroupEntity : Entity { + /// + /// Gets or sets the suite. + /// public required Guid Suite { get; init; } + /// + /// Gets or sets the status. + /// public required TestRunStatus Status { get; init; } + /// + /// Gets or sets the completed at. + /// public DateTimeOffset? CompletedAt { get; init; } + /// + /// Gets or sets the is system run. + /// public bool IsSystemRun { get; init; } + /// + /// Gets or sets the schedule id. + /// public Guid? ScheduleId { get; init; } + /// + /// Gets or sets the sample count. + /// public int SampleCount { get; init; } = 1; /// diff --git a/Proxytrace.Storage/Internal/Entities/TestRunGroup/TestRunGroupRepository.cs b/Proxytrace.Storage/Internal/Entities/TestRunGroup/TestRunGroupRepository.cs index 6ddcdcf29..3e8d7a112 100644 --- a/Proxytrace.Storage/Internal/Entities/TestRunGroup/TestRunGroupRepository.cs +++ b/Proxytrace.Storage/Internal/Entities/TestRunGroup/TestRunGroupRepository.cs @@ -13,6 +13,9 @@ namespace Proxytrace.Storage.Internal.Entities.TestRunGroup; [UsedImplicitly] internal class TestRunGroupRepository : AbstractRepository, ITestRunGroupRepository { + /// + /// Initializes a new instance of the class. + /// public TestRunGroupRepository( IMapper mapper, Func contextFactory, @@ -22,6 +25,9 @@ public TestRunGroupRepository( { } + /// + /// Gets the by agent asynchronously. + /// public async Task> GetByAgentAsync(Guid agentId, CancellationToken cancellationToken = default) { var context = contextFactory(); @@ -39,6 +45,9 @@ public async Task> GetByAgentAsync(Guid agentId, Ca return await Map(stored, cancellationToken); } + /// + /// Gets the by statuses asynchronously. + /// public async Task> GetByStatusesAsync( IReadOnlyCollection statuses, CancellationToken cancellationToken = default) @@ -56,6 +65,9 @@ public async Task> GetByStatusesAsync( return await Map(stored, cancellationToken); } + /// + /// Gets the pending optimization asynchronously. + /// public async Task> GetPendingOptimizationAsync( int limit, CancellationToken cancellationToken = default) @@ -82,6 +94,9 @@ public async Task> GetPendingOptimizationAsync( return await Map(stored, cancellationToken); } + /// + /// Gets the by project asynchronously. + /// public async Task> GetByProjectAsync(Guid projectId, CancellationToken cancellationToken = default) { var context = contextFactory(); @@ -103,6 +118,9 @@ public async Task> GetByProjectAsync(Guid projectId return await Map(stored, cancellationToken); } + /// + /// Gets the by agent paged asynchronously. + /// public async Task> GetByAgentPagedAsync( Guid agentId, int page, @@ -132,6 +150,9 @@ public async Task> GetByAgentPagedAsync( return new PagedResult(await Map(stored, cancellationToken), total, page, pageSize); } + /// + /// Gets the by project paged asynchronously. + /// public Task> GetByProjectPagedAsync( Guid projectId, int page, @@ -140,6 +161,9 @@ public Task> GetByProjectPagedAsync( CancellationToken cancellationToken = default) => GetByProjectsPagedAsync([projectId], page, pageSize, includeSystem, cancellationToken); + /// + /// Gets the by projects paged asynchronously. + /// public async Task> GetByProjectsPagedAsync( IReadOnlyCollection projectIds, int page, @@ -173,6 +197,9 @@ public async Task> GetByProjectsPagedAsync( return new PagedResult(await Map(stored, cancellationToken), total, page, pageSize); } + /// + /// Gets the by suite paged asynchronously. + /// public async Task> GetBySuitePagedAsync( Guid suiteId, int page, @@ -198,6 +225,9 @@ public async Task> GetBySuitePagedAsync( return new PagedResult(await Map(stored, cancellationToken), total, page, pageSize); } + /// + /// Counts the completed since asynchronously. + /// public async Task CountCompletedSinceAsync( Guid agentId, DateTimeOffset since, @@ -219,6 +249,9 @@ public async Task CountCompletedSinceAsync( .CountAsync(cancellationToken); } + /// + /// Gets the by schedule asynchronously. + /// public async Task> GetByScheduleAsync(Guid scheduleId, int take, CancellationToken cancellationToken = default) { var stored = await contextFactory() diff --git a/Proxytrace.Storage/Internal/Entities/TestRunSchedule/TestRunScheduleConfig.cs b/Proxytrace.Storage/Internal/Entities/TestRunSchedule/TestRunScheduleConfig.cs index 710152043..1be03442c 100644 --- a/Proxytrace.Storage/Internal/Entities/TestRunSchedule/TestRunScheduleConfig.cs +++ b/Proxytrace.Storage/Internal/Entities/TestRunSchedule/TestRunScheduleConfig.cs @@ -16,6 +16,9 @@ internal class TestRunScheduleConfig : AbstractEntityConfiguration contextFactory; + /// + /// Initializes a new instance of the class. + /// public TestRunScheduleConfig( IRepository suites, IRepository endpoints, @@ -28,6 +31,9 @@ public TestRunScheduleConfig( this.contextFactory = contextFactory; } + /// + /// Configures the application request pipeline. + /// public override void Configure(EntityTypeBuilder builder) { builder @@ -39,6 +45,9 @@ public override void Configure(EntityTypeBuilder builder) builder.HasIndex(e => new { e.IsEnabled, e.NextRunAt }); } + /// + /// Maps. + /// public async Task Map(TestRunScheduleEntity storedEntity, CancellationToken cancellationToken = default) { var context = contextFactory(); @@ -64,6 +73,9 @@ public async Task Map(TestRunScheduleEntity storedEntity, Canc existing: storedEntity); } + /// + /// Maps. + /// public Task Map(ITestRunSchedule domainEntity, CancellationToken cancellationToken = default) => new TestRunScheduleEntity { diff --git a/Proxytrace.Storage/Internal/Entities/TestRunSchedule/TestRunScheduleEndpointConfig.cs b/Proxytrace.Storage/Internal/Entities/TestRunSchedule/TestRunScheduleEndpointConfig.cs index d489e371c..74fc70908 100644 --- a/Proxytrace.Storage/Internal/Entities/TestRunSchedule/TestRunScheduleEndpointConfig.cs +++ b/Proxytrace.Storage/Internal/Entities/TestRunSchedule/TestRunScheduleEndpointConfig.cs @@ -6,6 +6,9 @@ namespace Proxytrace.Storage.Internal.Entities.TestRunSchedule; internal class TestRunScheduleEndpointConfig : AbstractEntityConfiguration { + /// + /// Configures the application request pipeline. + /// public override void Configure(EntityTypeBuilder builder) { builder.HasKey(e => new { e.ScheduleId, e.EndpointId }); diff --git a/Proxytrace.Storage/Internal/Entities/TestRunSchedule/TestRunScheduleEndpointEntity.cs b/Proxytrace.Storage/Internal/Entities/TestRunSchedule/TestRunScheduleEndpointEntity.cs index 730677c66..2f3389bf9 100644 --- a/Proxytrace.Storage/Internal/Entities/TestRunSchedule/TestRunScheduleEndpointEntity.cs +++ b/Proxytrace.Storage/Internal/Entities/TestRunSchedule/TestRunScheduleEndpointEntity.cs @@ -5,6 +5,12 @@ namespace Proxytrace.Storage.Internal.Entities.TestRunSchedule; /// internal record TestRunScheduleEndpointEntity { + /// + /// Gets or sets the schedule id. + /// public required Guid ScheduleId { get; init; } + /// + /// Gets or sets the endpoint id. + /// public required Guid EndpointId { get; init; } } diff --git a/Proxytrace.Storage/Internal/Entities/TestRunSchedule/TestRunScheduleEntity.cs b/Proxytrace.Storage/Internal/Entities/TestRunSchedule/TestRunScheduleEntity.cs index b519ac4cc..b5e696e25 100644 --- a/Proxytrace.Storage/Internal/Entities/TestRunSchedule/TestRunScheduleEntity.cs +++ b/Proxytrace.Storage/Internal/Entities/TestRunSchedule/TestRunScheduleEntity.cs @@ -5,12 +5,36 @@ namespace Proxytrace.Storage.Internal.Entities.TestRunSchedule; [StoredDomainEntity(typeof(ITestRunSchedule))] internal record TestRunScheduleEntity : Entity { + /// + /// Gets or sets the name. + /// public required string Name { get; init; } + /// + /// Gets or sets the suite. + /// public required Guid Suite { get; init; } + /// + /// Gets or sets the interval minutes. + /// public required int IntervalMinutes { get; init; } + /// + /// Gets or sets the is enabled. + /// public required bool IsEnabled { get; init; } + /// + /// Gets or sets the anchor at. + /// public required DateTimeOffset AnchorAt { get; init; } + /// + /// Gets or sets the next run at. + /// public required DateTimeOffset NextRunAt { get; init; } + /// + /// Gets or sets the last run at. + /// public DateTimeOffset? LastRunAt { get; init; } + /// + /// Gets or sets the schedule endpoints. + /// public required ICollection ScheduleEndpoints { get; init; } } diff --git a/Proxytrace.Storage/Internal/Entities/TestRunSchedule/TestRunScheduleRepository.cs b/Proxytrace.Storage/Internal/Entities/TestRunSchedule/TestRunScheduleRepository.cs index d4ead517f..e20db7387 100644 --- a/Proxytrace.Storage/Internal/Entities/TestRunSchedule/TestRunScheduleRepository.cs +++ b/Proxytrace.Storage/Internal/Entities/TestRunSchedule/TestRunScheduleRepository.cs @@ -12,6 +12,9 @@ namespace Proxytrace.Storage.Internal.Entities.TestRunSchedule; [UsedImplicitly] internal class TestRunScheduleRepository : AbstractRepository, ITestRunScheduleRepository { + /// + /// Initializes a new instance of the class. + /// public TestRunScheduleRepository( IMapper mapper, Func contextFactory, @@ -21,6 +24,9 @@ public TestRunScheduleRepository( { } + /// + /// Gets the by agent asynchronously. + /// public async Task> GetByAgentAsync(Guid agentId, CancellationToken cancellationToken = default) { var context = contextFactory(); @@ -38,6 +44,9 @@ public async Task> GetByAgentAsync(Guid agentId, return await Map(stored, cancellationToken); } + /// + /// Gets the by project asynchronously. + /// public async Task> GetByProjectAsync(Guid projectId, CancellationToken cancellationToken = default) { var context = contextFactory(); @@ -59,6 +68,9 @@ public async Task> GetByProjectAsync(Guid projec return await Map(stored, cancellationToken); } + /// + /// Gets the due asynchronously. + /// public async Task> GetDueAsync(DateTimeOffset now, CancellationToken cancellationToken = default) { var stored = await contextFactory() diff --git a/Proxytrace.Storage/Internal/Entities/TestSuite/TestSuiteConfig.cs b/Proxytrace.Storage/Internal/Entities/TestSuite/TestSuiteConfig.cs index d3cc49411..c2decba12 100644 --- a/Proxytrace.Storage/Internal/Entities/TestSuite/TestSuiteConfig.cs +++ b/Proxytrace.Storage/Internal/Entities/TestSuite/TestSuiteConfig.cs @@ -20,6 +20,9 @@ internal class TestSuiteConfig : AbstractEntityConfiguration, I private readonly ISerializer serializer; private readonly Func contextFactory; + /// + /// Initializes a new instance of the class. + /// public TestSuiteConfig( IRepository agents, IRepository evaluators, @@ -36,6 +39,9 @@ public TestSuiteConfig( this.contextFactory = contextFactory; } + /// + /// Configures the application request pipeline. + /// public override void Configure(EntityTypeBuilder builder) { builder @@ -52,6 +58,9 @@ public override void Configure(EntityTypeBuilder builder) ); } + /// + /// Maps. + /// public async Task Map(TestSuiteEntity storedEntity, CancellationToken cancellationToken = default) { var context = contextFactory(); @@ -73,6 +82,9 @@ public async Task Map(TestSuiteEntity storedEntity, CancellationToke existing: storedEntity); } + /// + /// Maps. + /// public Task Map(ITestSuite domainEntity, CancellationToken cancellationToken = default) => new TestSuiteEntity { diff --git a/Proxytrace.Storage/Internal/Entities/TestSuite/TestSuiteEvaluatorConfig.cs b/Proxytrace.Storage/Internal/Entities/TestSuite/TestSuiteEvaluatorConfig.cs index e91fcf881..ab16a8f0d 100644 --- a/Proxytrace.Storage/Internal/Entities/TestSuite/TestSuiteEvaluatorConfig.cs +++ b/Proxytrace.Storage/Internal/Entities/TestSuite/TestSuiteEvaluatorConfig.cs @@ -6,6 +6,9 @@ namespace Proxytrace.Storage.Internal.Entities.TestSuite; internal class TestSuiteEvaluatorConfig : AbstractEntityConfiguration { + /// + /// Configures the application request pipeline. + /// public override void Configure(EntityTypeBuilder builder) { builder.HasKey(e => new { e.TestSuiteId, e.EvaluatorId }); diff --git a/Proxytrace.Storage/Internal/Entities/TestSuite/TestSuiteEvaluatorEntity.cs b/Proxytrace.Storage/Internal/Entities/TestSuite/TestSuiteEvaluatorEntity.cs index c781a6be0..b3839b03a 100644 --- a/Proxytrace.Storage/Internal/Entities/TestSuite/TestSuiteEvaluatorEntity.cs +++ b/Proxytrace.Storage/Internal/Entities/TestSuite/TestSuiteEvaluatorEntity.cs @@ -6,6 +6,12 @@ namespace Proxytrace.Storage.Internal.Entities.TestSuite; /// internal record TestSuiteEvaluatorEntity { + /// + /// Gets or sets the test suite id. + /// public required Guid TestSuiteId { get; init; } + /// + /// Gets or sets the evaluator id. + /// public required Guid EvaluatorId { get; init; } } diff --git a/Proxytrace.Storage/Internal/Entities/TestSuite/TestSuiteRepository.cs b/Proxytrace.Storage/Internal/Entities/TestSuite/TestSuiteRepository.cs index e1f4858a5..a4fd07361 100644 --- a/Proxytrace.Storage/Internal/Entities/TestSuite/TestSuiteRepository.cs +++ b/Proxytrace.Storage/Internal/Entities/TestSuite/TestSuiteRepository.cs @@ -12,6 +12,9 @@ namespace Proxytrace.Storage.Internal.Entities.TestSuite; [UsedImplicitly] internal class TestSuiteRepository : AbstractRepository, ITestSuiteRepository { + /// + /// Initializes a new instance of the class. + /// public TestSuiteRepository( IMapper mapper, Func contextFactory, @@ -21,6 +24,9 @@ public TestSuiteRepository( { } + /// + /// Gets the by agent asynchronously. + /// public async Task> GetByAgentAsync(Guid agentId, CancellationToken cancellationToken = default) { var stored = await contextFactory() @@ -32,6 +38,9 @@ public async Task> GetByAgentAsync(Guid agentId, Cance return await Map(stored, cancellationToken); } + /// + /// Gets the by project asynchronously. + /// public async Task> GetByProjectAsync(Guid projectId, CancellationToken cancellationToken = default) { var context = contextFactory(); @@ -49,6 +58,9 @@ public async Task> GetByProjectAsync(Guid projectId, C return await Map(stored, cancellationToken); } + /// + /// Gets the by agent paged asynchronously. + /// public async Task> GetByAgentPagedAsync( Guid agentId, int page, @@ -71,6 +83,9 @@ public async Task> GetByAgentPagedAsync( return new PagedResult(await Map(stored, cancellationToken), total, page, pageSize); } + /// + /// Gets the by project paged asynchronously. + /// public Task> GetByProjectPagedAsync( Guid projectId, int page, @@ -78,6 +93,9 @@ public Task> GetByProjectPagedAsync( CancellationToken cancellationToken = default) => GetByProjectsPagedAsync([projectId], page, pageSize, cancellationToken); + /// + /// Gets the by projects paged asynchronously. + /// public async Task> GetByProjectsPagedAsync( IReadOnlyCollection projectIds, int page, @@ -106,6 +124,9 @@ public async Task> GetByProjectsPagedAsync( return new PagedResult(await Map(stored, cancellationToken), total, page, pageSize); } + /// + /// Gets the project id by test case asynchronously. + /// public async Task GetProjectIdByTestCaseAsync(Guid testCaseId, CancellationToken cancellationToken = default) { var context = contextFactory(); diff --git a/Proxytrace.Storage/Internal/Entities/User/UserConfig.cs b/Proxytrace.Storage/Internal/Entities/User/UserConfig.cs index d6f59e302..358fa03a7 100644 --- a/Proxytrace.Storage/Internal/Entities/User/UserConfig.cs +++ b/Proxytrace.Storage/Internal/Entities/User/UserConfig.cs @@ -10,11 +10,17 @@ internal class UserConfig : AbstractEntityConfiguration, IMapper + /// Initializes a new instance of the class. + /// public UserConfig(IUser.CreateExisting factory) { this.factory = factory; } + /// + /// Configures the application request pipeline. + /// public override void Configure(EntityTypeBuilder builder) { builder.HasIndex(e => e.Email).IsUnique(); @@ -30,9 +36,15 @@ public override void Configure(EntityTypeBuilder builder) builder.Property(e => e.EmailNotificationMinSeverity).HasDefaultValue(NotificationSeverity.Info); } + /// + /// Maps. + /// public Task Map(UserEntity stored, CancellationToken cancellationToken = default) => factory(stored.Email, stored.ExternalSubject, stored.PasswordHash, stored.Role, stored.Language, stored.EmailNotificationsEnabled, stored.EmailNotificationMinSeverity, stored).ToTaskResult(); + /// + /// Maps. + /// public Task Map(IUser domain, CancellationToken cancellationToken = default) => new UserEntity { diff --git a/Proxytrace.Storage/Internal/Entities/User/UserEntity.cs b/Proxytrace.Storage/Internal/Entities/User/UserEntity.cs index 3625fbadb..59fef1e5a 100644 --- a/Proxytrace.Storage/Internal/Entities/User/UserEntity.cs +++ b/Proxytrace.Storage/Internal/Entities/User/UserEntity.cs @@ -7,6 +7,9 @@ namespace Proxytrace.Storage.Internal.Entities.User; [Cacheable] internal record UserEntity : Entity { + /// + /// Gets or sets the email. + /// public required string Email { get; init; } /// . Null for local-auth users. @@ -15,6 +18,9 @@ internal record UserEntity : Entity /// . Null for OIDC users. public string? PasswordHash { get; init; } + /// + /// Gets or sets the role. + /// public required UserRole Role { get; init; } /// . BCP-47 culture code; defaults to English. diff --git a/Proxytrace.Storage/Internal/Entities/User/UserRepository.cs b/Proxytrace.Storage/Internal/Entities/User/UserRepository.cs index 5de999b04..1e91c76ab 100644 --- a/Proxytrace.Storage/Internal/Entities/User/UserRepository.cs +++ b/Proxytrace.Storage/Internal/Entities/User/UserRepository.cs @@ -9,6 +9,9 @@ namespace Proxytrace.Storage.Internal.Entities.User; [UsedImplicitly] internal class UserRepository : AbstractRepository, IUserRepository { + /// + /// Initializes a new instance of the class. + /// public UserRepository( IMapper mapper, Func context, @@ -16,6 +19,9 @@ public UserRepository( IEntityEventService entityEvents, AmbientDbContext ambient) : base(mapper, context, transaction, entityEvents, ambient) { } + /// + /// Finds the by external subject asynchronously. + /// public async Task FindByExternalSubjectAsync(string externalSubject, CancellationToken cancellationToken = default) { var entity = await contextFactory().Set().AsNoTracking() @@ -24,6 +30,9 @@ public UserRepository( return await Map(entity, cancellationToken); } + /// + /// Finds the by email asynchronously. + /// public async Task FindByEmailAsync(string email, CancellationToken cancellationToken = default) { // Emails are normalized (trimmed, invariant-lowercase) at the write boundary (see User ctor), @@ -37,6 +46,9 @@ public UserRepository( return await Map(entity, cancellationToken); } + /// + /// Counts the by role asynchronously. + /// public async Task CountByRoleAsync(UserRole role, CancellationToken cancellationToken = default) => await contextFactory().Set().AsNoTracking() .CountAsync(x => x.Role == role, cancellationToken); diff --git a/Proxytrace.Storage/Internal/Entities/UserTotpEnrollment/UserTotpEnrollmentConfig.cs b/Proxytrace.Storage/Internal/Entities/UserTotpEnrollment/UserTotpEnrollmentConfig.cs index 2230cb1e0..bcc73c62d 100644 --- a/Proxytrace.Storage/Internal/Entities/UserTotpEnrollment/UserTotpEnrollmentConfig.cs +++ b/Proxytrace.Storage/Internal/Entities/UserTotpEnrollment/UserTotpEnrollmentConfig.cs @@ -23,6 +23,9 @@ internal class UserTotpEnrollmentConfig private readonly Lazy protector; private readonly ILogger logger; + /// + /// Initializes a new instance of the class. + /// public UserTotpEnrollmentConfig( IUserTotpEnrollment.CreateExisting factory, IRepository users, @@ -35,6 +38,9 @@ public UserTotpEnrollmentConfig( this.logger = logger; } + /// + /// Configures the application request pipeline. + /// public override void Configure(EntityTypeBuilder builder) { // One enrollment per user — re-running setup replaces the row, never accumulates. @@ -47,12 +53,18 @@ public override void Configure(EntityTypeBuilder build .OnDelete(DeleteBehavior.Cascade); } + /// + /// Maps. + /// public async Task Map(UserTotpEnrollmentEntity stored, CancellationToken cancellationToken = default) { var user = await users.GetAsync(stored.User, cancellationToken); return factory(user, Decrypt(stored.Secret), stored.ConfirmedAt, stored.LastUsedStep, stored); } + /// + /// Maps. + /// public Task Map(IUserTotpEnrollment domain, CancellationToken cancellationToken = default) => new UserTotpEnrollmentEntity { diff --git a/Proxytrace.Storage/Internal/Entities/UserTotpEnrollment/UserTotpEnrollmentEntity.cs b/Proxytrace.Storage/Internal/Entities/UserTotpEnrollment/UserTotpEnrollmentEntity.cs index 2b57d022c..5597b9dd7 100644 --- a/Proxytrace.Storage/Internal/Entities/UserTotpEnrollment/UserTotpEnrollmentEntity.cs +++ b/Proxytrace.Storage/Internal/Entities/UserTotpEnrollment/UserTotpEnrollmentEntity.cs @@ -5,6 +5,9 @@ namespace Proxytrace.Storage.Internal.Entities.UserTotpEnrollment; [StoredDomainEntity(typeof(IUserTotpEnrollment))] internal record UserTotpEnrollmentEntity : Entity { + /// + /// Gets or sets the user. + /// public required Guid User { get; init; } /// @@ -13,6 +16,12 @@ internal record UserTotpEnrollmentEntity : Entity /// public required string Secret { get; init; } + /// + /// Gets or sets the confirmed at. + /// public DateTimeOffset? ConfirmedAt { get; init; } + /// + /// Gets or sets the last used step. + /// public long? LastUsedStep { get; init; } } diff --git a/Proxytrace.Storage/Internal/Entities/UserTotpEnrollment/UserTotpEnrollmentRepository.cs b/Proxytrace.Storage/Internal/Entities/UserTotpEnrollment/UserTotpEnrollmentRepository.cs index e3512f1b6..3696eba89 100644 --- a/Proxytrace.Storage/Internal/Entities/UserTotpEnrollment/UserTotpEnrollmentRepository.cs +++ b/Proxytrace.Storage/Internal/Entities/UserTotpEnrollment/UserTotpEnrollmentRepository.cs @@ -11,6 +11,9 @@ internal class UserTotpEnrollmentRepository : AbstractRepository, IUserTotpEnrollmentRepository { + /// + /// Initializes a new instance of the class. + /// public UserTotpEnrollmentRepository( IMapper mapper, Func context, @@ -20,6 +23,9 @@ public UserTotpEnrollmentRepository( { } + /// + /// Finds the by user asynchronously. + /// public async Task FindByUserAsync(Guid userId, CancellationToken cancellationToken = default) { var entity = await contextFactory().Set().AsNoTracking() @@ -28,6 +34,9 @@ public UserTotpEnrollmentRepository( return await Map(entity, cancellationToken); } + /// + /// Lists the confirmed user ids asynchronously. + /// public async Task> ListConfirmedUserIdsAsync(CancellationToken cancellationToken = default) => await contextFactory().Set().AsNoTracking() .Where(x => x.ConfirmedAt != null) diff --git a/Proxytrace.Storage/Internal/EvaluationStatBackfillService.cs b/Proxytrace.Storage/Internal/EvaluationStatBackfillService.cs index 0bf0d9644..6db197737 100644 --- a/Proxytrace.Storage/Internal/EvaluationStatBackfillService.cs +++ b/Proxytrace.Storage/Internal/EvaluationStatBackfillService.cs @@ -41,6 +41,9 @@ internal sealed class EvaluationStatBackfillService : IHostedService // batchSize/retryDelay default to production values and are constructor-injectable purely so tests can // exercise the multi-batch loop and the retry/never-throw wrapper without large data or waits. Autofac // supplies only contextFactory/logger and uses these defaults for the optional parameters. + /// + /// Initializes a new instance of the class. + /// public EvaluationStatBackfillService( Func> contextFactory, ILogger logger, @@ -53,6 +56,9 @@ public EvaluationStatBackfillService( this.retryDelay = retryDelay ?? TimeSpan.FromSeconds(2); } + /// + /// Starts asynchronously. + /// public async Task StartAsync(CancellationToken cancellationToken) { for (var attempt = 1; attempt <= MaxAttempts; attempt++) @@ -166,5 +172,8 @@ public async Task BackfillAsync(CancellationToken cancellationToken) return total; } + /// + /// Stops asynchronously. + /// public Task StopAsync(CancellationToken cancellationToken) => Task.CompletedTask; } diff --git a/Proxytrace.Storage/Internal/PostgresConfiguration.cs b/Proxytrace.Storage/Internal/PostgresConfiguration.cs index 1c076ce5f..4f1eb2116 100644 --- a/Proxytrace.Storage/Internal/PostgresConfiguration.cs +++ b/Proxytrace.Storage/Internal/PostgresConfiguration.cs @@ -4,5 +4,8 @@ internal record PostgresConfiguration : StorageConfiguration { internal override bool SupportsMigrations => true; + /// + /// Gets or sets the connection string. + /// public required string ConnectionString { get; init; } } diff --git a/Proxytrace.Storage/Internal/SecretsBackfillService.cs b/Proxytrace.Storage/Internal/SecretsBackfillService.cs index 2aed43f78..7b16d4c90 100644 --- a/Proxytrace.Storage/Internal/SecretsBackfillService.cs +++ b/Proxytrace.Storage/Internal/SecretsBackfillService.cs @@ -53,6 +53,9 @@ internal sealed class SecretsBackfillService : IHostedService private readonly ILogger logger; private readonly ILogger audit; + /// + /// Initializes a new instance of the class. + /// public SecretsBackfillService( Func> contextFactory, ISecretProtector protector, @@ -70,6 +73,9 @@ public SecretsBackfillService( private const int MaxAttempts = 3; private static readonly TimeSpan RetryDelay = TimeSpan.FromSeconds(2); + /// + /// Starts asynchronously. + /// public async Task StartAsync(CancellationToken cancellationToken) { // Until a row is backfilled its lookup column still holds the pre-retrofit plaintext, so the @@ -270,5 +276,8 @@ private async Task BackfillInvitesAsync(CancellationToken cancellationToken return rows.Count; } + /// + /// Stops asynchronously. + /// public Task StopAsync(CancellationToken cancellationToken) => Task.CompletedTask; } diff --git a/Proxytrace.Storage/Internal/Statistics/AgentCallStatsQueries.cs b/Proxytrace.Storage/Internal/Statistics/AgentCallStatsQueries.cs index c020a739c..7c72b2293 100644 --- a/Proxytrace.Storage/Internal/Statistics/AgentCallStatsQueries.cs +++ b/Proxytrace.Storage/Internal/Statistics/AgentCallStatsQueries.cs @@ -33,6 +33,9 @@ internal class AgentCallStatsQueries : IAgentCallStatsReader private readonly IMapper endpointMapper; private readonly ILogger logger; + /// + /// Initializes a new instance of the class. + /// public AgentCallStatsQueries( Func contextFactory, IMapper endpointMapper, @@ -43,6 +46,9 @@ public AgentCallStatsQueries( this.logger = logger; } + /// + /// Gets the summary asynchronously. + /// public async Task GetSummaryAsync(StatisticsFilter filter, CancellationToken cancellationToken = default) { StorageDbContext context = contextFactory(); @@ -73,6 +79,9 @@ public async Task GetSummaryAsync(StatisticsFilter filter, Ca OverallPassRate: null); } + /// + /// Gets the earliest call asynchronously. + /// public async Task GetEarliestCallAsync(StatisticsFilter filter, CancellationToken cancellationToken = default) { StorageDbContext context = contextFactory(); @@ -82,6 +91,9 @@ public async Task GetSummaryAsync(StatisticsFilter filter, Ca .FirstOrDefaultAsync(cancellationToken); } + /// + /// Gets the token usage asynchronously. + /// public async Task> GetTokenUsageAsync(StatisticsFilter filter, StatisticsBucket bucket, CancellationToken cancellationToken = default) { StorageDbContext context = contextFactory(); @@ -119,6 +131,9 @@ public async Task> GetTokenUsageAsync(StatisticsFi .ToArray(); } + /// + /// Gets the latency asynchronously. + /// public async Task> GetLatencyAsync(StatisticsFilter filter, CancellationToken cancellationToken = default) { StorageDbContext context = contextFactory(); @@ -273,6 +288,9 @@ internal static (string Where, IReadOnlyList<(string Name, object Value)> Parame return (where, parameters); } + /// + /// Gets the error rates asynchronously. + /// public async Task> GetErrorRatesAsync(StatisticsFilter filter, CancellationToken cancellationToken = default) { StorageDbContext context = contextFactory(); @@ -298,6 +316,9 @@ public async Task> GetErrorRatesAsync(StatisticsFil .ToArray(); } + /// + /// Gets the model breakdown asynchronously. + /// public async Task> GetModelBreakdownAsync(StatisticsFilter filter, CancellationToken cancellationToken = default) { StorageDbContext context = contextFactory(); @@ -334,6 +355,9 @@ public async Task> GetModelBreakdownAsync(Stat .ToArray(); } + /// + /// Gets the agent breakdown asynchronously. + /// public async Task> GetAgentBreakdownAsync(StatisticsFilter filter, CancellationToken cancellationToken = default) { StorageDbContext context = contextFactory(); @@ -350,6 +374,9 @@ public async Task> GetAgentBreakdownAsync(Stat return rows.OrderByDescending(r => r.CallCount).ThenBy(r => r.AgentId).ToArray(); } + /// + /// Gets the cost estimate asynchronously. + /// public async Task> GetCostEstimateAsync(StatisticsFilter filter, CancellationToken cancellationToken = default) { StorageDbContext context = contextFactory(); @@ -393,6 +420,9 @@ public async Task> GetCostEstimateAsync(Statisti .ToArray(); } + /// + /// Gets the cost by project and agent asynchronously. + /// public async Task> GetCostByProjectAndAgentAsync( StatisticsFilter filter, CancellationToken cancellationToken = default) @@ -436,6 +466,9 @@ public async Task> GetCostByProjectAndAgentA .ToArray(); } + /// + /// Gets the cost series by agent asynchronously. + /// public async Task> GetCostSeriesByAgentAsync( StatisticsFilter filter, StatisticsBucket bucket, @@ -483,6 +516,9 @@ public async Task> GetCostSeriesByAgentAsync( .ToArray(); } + /// + /// Gets the cost by api key asynchronously. + /// public async Task> GetCostByApiKeyAsync( StatisticsFilter filter, CancellationToken cancellationToken = default) @@ -527,6 +563,9 @@ public async Task> GetCostByApiKeyAsync( .ToArray(); } + /// + /// Gets the cost series by api key asynchronously. + /// public async Task> GetCostSeriesByApiKeyAsync( StatisticsFilter filter, StatisticsBucket bucket, @@ -572,6 +611,9 @@ public async Task> GetCostSeriesByApiKeyAsync( .ToArray(); } + /// + /// Determines whether the unpriced endpoints asynchronously. + /// public async Task HasUnpricedEndpointsAsync( StatisticsFilter filter, CancellationToken cancellationToken = default) @@ -607,6 +649,9 @@ private static decimal CostOf( (ulong)Math.Max(input, 0L), (ulong)Math.Max(output, 0L), (ulong)Math.Max(cached, 0L))) ?? 0m : 0m; + /// + /// Gets the token usage by agent asynchronously. + /// public async Task> GetTokenUsageByAgentAsync(StatisticsFilter filter, StatisticsBucket bucket, CancellationToken cancellationToken = default) { StorageDbContext context = contextFactory(); @@ -652,6 +697,9 @@ public async Task> GetTokenUsageByAgentAsync( private const OutlierFlags StaticOutlierBits = OutlierFlags.HighTokens | OutlierFlags.HighLatency | OutlierFlags.LowCacheHit | OutlierFlags.ManyToolCalls; + /// + /// Gets the anomaly counts by agent asynchronously. + /// public async Task> GetAnomalyCountsByAgentAsync(StatisticsFilter filter, StatisticsBucket bucket, CancellationToken cancellationToken = default) { StorageDbContext context = contextFactory(); @@ -693,6 +741,9 @@ public async Task> GetAnomalyCountsByAgentAsync( .ToArray(); } + /// + /// Gets the live telemetry asynchronously. + /// public async Task GetLiveTelemetryAsync(StatisticsFilter filter, DateTimeOffset since, DateTimeOffset now, CancellationToken cancellationToken = default) { StorageDbContext context = contextFactory(); @@ -775,6 +826,9 @@ WITHIN GROUP (ORDER BY "LatencyMs") } } + /// + /// Gets the call trends asynchronously. + /// public async Task GetCallTrendsAsync(StatisticsFilter filter, int buckets, DateTimeOffset from, DateTimeOffset to, CancellationToken cancellationToken = default) { buckets = Math.Max(buckets, 1); @@ -824,6 +878,9 @@ public async Task GetCallTrendsAsync(StatisticsFilter filter, int bu return new CallTrends(traces, latencyMs, throughput); } + /// + /// Gets the pulse asynchronously. + /// public async Task> GetPulseAsync(StatisticsFilter filter, DateTimeOffset from, DateTimeOffset to, int buckets, CancellationToken cancellationToken = default) { buckets = Math.Max(buckets, 1); @@ -850,6 +907,9 @@ public async Task> GetPulseAsync(StatisticsFilter filter, Dat return counts; } + /// + /// Gets the agent time series asynchronously. + /// public async Task> GetAgentTimeSeriesAsync( Guid agentId, DateTimeOffset from, @@ -861,6 +921,9 @@ public async Task> GetAgentTimeSeriesAsync( return series; } + /// + /// Gets the agent window asynchronously. + /// public async Task<(IReadOnlyList Series, AgentTimeSummary Summary)> GetAgentWindowAsync( Guid agentId, DateTimeOffset from, @@ -937,6 +1000,9 @@ decimal CostOf(Guid endpointId, long input, long output, long cached) return (series, summary); } + /// + /// Gets the agent distributions asynchronously. + /// public async Task GetAgentDistributionsAsync( Guid agentId, DateTimeOffset from, DateTimeOffset to, CancellationToken cancellationToken = default) { diff --git a/Proxytrace.Storage/Internal/Statistics/EvaluatorStatsQueries.cs b/Proxytrace.Storage/Internal/Statistics/EvaluatorStatsQueries.cs index fb25f6842..f91390014 100644 --- a/Proxytrace.Storage/Internal/Statistics/EvaluatorStatsQueries.cs +++ b/Proxytrace.Storage/Internal/Statistics/EvaluatorStatsQueries.cs @@ -12,11 +12,17 @@ internal class EvaluatorStatsQueries : IEvaluatorStatsReader { private readonly Func contextFactory; + /// + /// Initializes a new instance of the class. + /// public EvaluatorStatsQueries(Func contextFactory) { this.contextFactory = contextFactory; } + /// + /// Gets the overview asynchronously. + /// public async Task GetOverviewAsync( Guid evaluatorId, DateTimeOffset from, @@ -60,6 +66,9 @@ public async Task GetOverviewAsync( return BuildOverview(rows, bucket); } + /// + /// Gets the sparklines asynchronously. + /// public async Task> GetSparklinesAsync( IReadOnlyCollection projectIds, DateTimeOffset from, diff --git a/Proxytrace.Storage/Internal/Statistics/OutlierBaselineQueries.cs b/Proxytrace.Storage/Internal/Statistics/OutlierBaselineQueries.cs index 5e25850f9..761f6eeb5 100644 --- a/Proxytrace.Storage/Internal/Statistics/OutlierBaselineQueries.cs +++ b/Proxytrace.Storage/Internal/Statistics/OutlierBaselineQueries.cs @@ -16,11 +16,17 @@ internal sealed class OutlierBaselineQueries : IOutlierBaselineReader { private readonly Func contextFactory; + /// + /// Initializes a new instance of the class. + /// public OutlierBaselineQueries(Func contextFactory) { this.contextFactory = contextFactory; } + /// + /// Gets the baseline asynchronously. + /// public async Task GetBaselineAsync( Guid agentId, int sampleWindow, CancellationToken cancellationToken = default) { diff --git a/Proxytrace.Storage/Internal/Statistics/TestRunStatsStore.cs b/Proxytrace.Storage/Internal/Statistics/TestRunStatsStore.cs index 4ce7a953f..419dc338a 100644 --- a/Proxytrace.Storage/Internal/Statistics/TestRunStatsStore.cs +++ b/Proxytrace.Storage/Internal/Statistics/TestRunStatsStore.cs @@ -14,12 +14,18 @@ internal class TestRunStatsStore : IStatsReader contextFactory; private readonly ITransaction transaction; + /// + /// Initializes a new instance of the class. + /// public TestRunStatsStore(Func contextFactory, ITransaction transaction) { this.contextFactory = contextFactory; this.transaction = transaction; } + /// + /// Upsert asynchronously. + /// public Task UpsertAsync(TestRunStats stats, CancellationToken cancellationToken = default) => transaction.InvokeAsync(async () => { @@ -93,6 +99,9 @@ private Task ExistsAsync(Guid testRunId, CancellationToken cancellationTok .AsNoTracking() .AnyAsync(e => e.TestRunId == testRunId, cancellationToken); + /// + /// Removes asynchronously. + /// public Task RemoveAsync(Guid testRunId, CancellationToken cancellationToken = default) => transaction.InvokeAsync(async () => { @@ -108,6 +117,9 @@ public Task RemoveAsync(Guid testRunId, CancellationToken cancellationToken = de await context.SaveChangesAsync(cancellationToken); }); + /// + /// Finds asynchronously. + /// public async Task FindAsync(Guid testRunId, CancellationToken cancellationToken = default) { TestRunStatsEntity? entity = await contextFactory() @@ -117,6 +129,9 @@ public Task RemoveAsync(Guid testRunId, CancellationToken cancellationToken = de return entity is null ? null : ToDto(entity); } + /// + /// Query asynchronously. + /// public async Task> QueryAsync(TestRunStats.Filter filter, CancellationToken cancellationToken = default) { IQueryable q = Query(contextFactory(), filter); @@ -125,6 +140,9 @@ public async Task> QueryAsync(TestRunStats.Filter fi return rows.Select(ToDto).ToArray(); } + /// + /// Gets the pass totals asynchronously. + /// public async Task GetPassTotalsAsync(TestRunStats.Filter filter, CancellationToken cancellationToken = default) { // A single scalar aggregate row crosses the wire — never the O(all-history) row set the @@ -141,6 +159,9 @@ public async Task GetPassTotalsAsync(TestRunStats.Filter filt return new TestRunPassTotals(TotalCases: agg?.Cases ?? 0, TotalPassed: agg?.Passed ?? 0); } + /// + /// Gets the recent cohorts asynchronously. + /// public async Task> GetRecentCohortsAsync(TestRunStats.Filter filter, int limit, CancellationToken cancellationToken = default) { limit = Math.Max(limit, 1); diff --git a/Proxytrace.Storage/Internal/TestDataReset.cs b/Proxytrace.Storage/Internal/TestDataReset.cs index 1665422c0..294a8d404 100644 --- a/Proxytrace.Storage/Internal/TestDataReset.cs +++ b/Proxytrace.Storage/Internal/TestDataReset.cs @@ -25,11 +25,17 @@ TRUNCATE TABLE private readonly Func contextFactory; + /// + /// Initializes a new instance of the class. + /// public TestDataReset(Func contextFactory) { this.contextFactory = contextFactory; } + /// + /// Reset asynchronously. + /// public async Task ResetAsync(CancellationToken cancellationToken = default) => await contextFactory().Database.ExecuteSqlRawAsync(TruncateSql, cancellationToken); } diff --git a/Proxytrace.Storage/Module.cs b/Proxytrace.Storage/Module.cs index 6d2b1c5f3..3b2a968fa 100644 --- a/Proxytrace.Storage/Module.cs +++ b/Proxytrace.Storage/Module.cs @@ -26,6 +26,9 @@ public sealed class Module : Autofac.Module private readonly Func configurationFactory; private readonly bool registerApplicationServices; + /// + /// Initializes a new instance of the class. + /// public Module( Func configurationFactory, bool registerApplicationServices = true) diff --git a/Proxytrace.Storage/StorageConfiguration.cs b/Proxytrace.Storage/StorageConfiguration.cs index 51548af26..266969109 100644 --- a/Proxytrace.Storage/StorageConfiguration.cs +++ b/Proxytrace.Storage/StorageConfiguration.cs @@ -2,6 +2,9 @@ namespace Proxytrace.Storage; +/// +/// Configuration for storage. +/// public abstract record StorageConfiguration { /// diff --git a/Proxytrace.Storage/StorageDbContext.cs b/Proxytrace.Storage/StorageDbContext.cs index 5f79d22ec..4c25a8a6d 100644 --- a/Proxytrace.Storage/StorageDbContext.cs +++ b/Proxytrace.Storage/StorageDbContext.cs @@ -11,6 +11,9 @@ namespace Proxytrace.Storage; /// internal sealed class StorageDbContext : NordsteinDbContext { + /// + /// Initializes a new instance of the class. + /// public StorageDbContext( IEnumerable configurations, DbContextOptions options) diff --git a/Proxytrace.Storage/StorageDbContextFactory.cs b/Proxytrace.Storage/StorageDbContextFactory.cs index cbad09b09..079ae9552 100644 --- a/Proxytrace.Storage/StorageDbContextFactory.cs +++ b/Proxytrace.Storage/StorageDbContextFactory.cs @@ -20,6 +20,9 @@ namespace Proxytrace.Storage; [UsedImplicitly] internal class StorageDbContextFactory : IDesignTimeDbContextFactory { + /// + /// Creates the db context. + /// public StorageDbContext CreateDbContext(string[] args) { // Environment variables are added LAST so they win over the JSON files — this is what makes @@ -73,16 +76,34 @@ public StorageDbContext CreateDbContext(string[] args) /// private sealed class DesignTimeSecretSeam : ISecretProtector, ISecretHasher, ISecretIndexer { + /// + /// Protect. + /// public string Protect(string plaintext) => plaintext; + /// + /// Unprotect. + /// public string Unprotect(string ciphertext) => ciphertext; + /// + /// Hashes. + /// public string Hash(string value) => value; + /// + /// Indexes. + /// public string Index(string value) => value; + /// + /// Legacy index. + /// public string LegacyIndex(string value) => value; + /// + /// Gets the is keyed. + /// public bool IsKeyed => false; } @@ -93,6 +114,9 @@ private sealed class DesignTimeSecretSeam : ISecretProtector, ISecretHasher, ISe /// private sealed class DesignTimeAgentNameGenerator : IAgentNameGenerator { + /// + /// Generates the name asynchronously. + /// public Task GenerateNameAsync( IPromptTemplate systemPrompt, IProject project, @@ -107,9 +131,15 @@ public Task GenerateNameAsync( /// private sealed class DesignTimeProviderClient : IProviderClient { + /// + /// Verifies the connection asynchronously. + /// public Task VerifyConnectionAsync(CancellationToken cancellationToken = default) => throw new NotSupportedException("Provider client operations are not available at design time."); + /// + /// Gets the models asynchronously. + /// public Task> GetModelsAsync(CancellationToken cancellationToken = default) => throw new NotSupportedException("Provider client operations are not available at design time."); } From ce946edc9d39f33fb38ef07387b738aeb367a5ac Mon Sep 17 00:00:00 2001 From: Claude Date: Sat, 15 Aug 2026 21:44:26 +0000 Subject: [PATCH 2/5] docs: add XML docstrings to public members in Client.Sample MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Missed in the initial commit — same 3-line block format. Co-Authored-By: Claude Sonnet 4.6 Claude-Session: https://claude.ai/code/session_01NaEvHNpxKuuKG1k1SQw957 --- Proxytrace.Client.Sample/Configuration.cs | 9 +++++++++ Proxytrace.Client.Sample/Internal/AgentCallSimulator.cs | 6 ++++++ Proxytrace.Client.Sample/Internal/ToolCallSimulator.cs | 6 ++++++ Proxytrace.Client.Sample/Module.cs | 3 +++ 4 files changed, 24 insertions(+) diff --git a/Proxytrace.Client.Sample/Configuration.cs b/Proxytrace.Client.Sample/Configuration.cs index e76fc070b..fdec9963c 100644 --- a/Proxytrace.Client.Sample/Configuration.cs +++ b/Proxytrace.Client.Sample/Configuration.cs @@ -1,7 +1,16 @@ namespace Proxytrace.Client.Sample; +/// +/// Configuration for . +/// public record Configuration { + /// + /// Gets or sets the endpoint. + /// public required string Endpoint { get; init; } + /// + /// Gets or sets the api key. + /// public required string ApiKey { get; init; } } diff --git a/Proxytrace.Client.Sample/Internal/AgentCallSimulator.cs b/Proxytrace.Client.Sample/Internal/AgentCallSimulator.cs index 2bf7bfff4..2e8c53381 100644 --- a/Proxytrace.Client.Sample/Internal/AgentCallSimulator.cs +++ b/Proxytrace.Client.Sample/Internal/AgentCallSimulator.cs @@ -12,11 +12,17 @@ internal class AgentCallSimulator { private readonly Configuration configuration; + /// + /// Initializes a new instance of the class. + /// public AgentCallSimulator(Configuration configuration) { this.configuration = configuration; } + /// + /// Runs. + /// public async Task Run(CancellationToken cancellationToken = default) { using var httpClient = new HttpClient(); diff --git a/Proxytrace.Client.Sample/Internal/ToolCallSimulator.cs b/Proxytrace.Client.Sample/Internal/ToolCallSimulator.cs index 902106fdc..844227c9e 100644 --- a/Proxytrace.Client.Sample/Internal/ToolCallSimulator.cs +++ b/Proxytrace.Client.Sample/Internal/ToolCallSimulator.cs @@ -17,11 +17,17 @@ internal class ToolCallSimulator private readonly Configuration configuration; + /// + /// Initializes a new instance of the class. + /// public ToolCallSimulator(Configuration configuration) { this.configuration = configuration; } + /// + /// Runs. + /// public async Task Run(CancellationToken cancellationToken = default) { using var httpClient = new HttpClient(); diff --git a/Proxytrace.Client.Sample/Module.cs b/Proxytrace.Client.Sample/Module.cs index 76996d391..4f973b1c7 100644 --- a/Proxytrace.Client.Sample/Module.cs +++ b/Proxytrace.Client.Sample/Module.cs @@ -4,6 +4,9 @@ namespace Proxytrace.Client.Sample; +/// +/// Autofac module that registers services. +/// public class Module : Autofac.Module { protected override void Load(ContainerBuilder builder) From 78d72c17a6b80c9147ebc979a1cff6318bb79b1d Mon Sep 17 00:00:00 2001 From: Claude Date: Sat, 15 Aug 2026 21:52:01 +0000 Subject: [PATCH 3/5] docs: improve docstrings with code-understanding summaries (partial, Api/Auth) Replace auto-generated name-derived summaries with contextual descriptions that reflect actual implementation behaviour (file paths, merge semantics, return conditions, etc.). Co-Authored-By: Claude Sonnet 4.6 Claude-Session: https://claude.ai/code/session_01NaEvHNpxKuuKG1k1SQw957 --- .../Auth/AppSettingsLocalSigningKeyStore.cs | 7 +++++-- Proxytrace.Api/Auth/AuthUserResolver.cs | 6 ++++-- .../Auth/DataDirectorySigningKeyStore.cs | 6 ++++-- Proxytrace.Api/Auth/IProjectAccessGuard.cs | 7 +++++-- Proxytrace.Api/Auth/JwtBearerEventsFactory.cs | 4 +++- Proxytrace.Api/Auth/SessionCookie.cs | 9 ++++++--- Proxytrace.Api/Auth/SigningKeyProvider.cs | 5 ++++- .../Configuration/SearchRequestOptions.cs | 12 +++++++----- Proxytrace.Api/Configuration/StatisticsOptions.cs | 15 ++++++++++----- 9 files changed, 48 insertions(+), 23 deletions(-) diff --git a/Proxytrace.Api/Auth/AppSettingsLocalSigningKeyStore.cs b/Proxytrace.Api/Auth/AppSettingsLocalSigningKeyStore.cs index 6907cc153..2a8535007 100644 --- a/Proxytrace.Api/Auth/AppSettingsLocalSigningKeyStore.cs +++ b/Proxytrace.Api/Auth/AppSettingsLocalSigningKeyStore.cs @@ -30,7 +30,8 @@ public AppSettingsLocalSigningKeyStore(IHostEnvironment environment) }; /// - /// Registers services with the Autofac container builder. + /// Reads Authentication:Local:SigningKey from appsettings.local.json in the + /// content root, returning when the file is absent or unparseable. /// public string? Load() { @@ -50,7 +51,9 @@ public AppSettingsLocalSigningKeyStore(IHostEnvironment environment) } /// - /// Persist. + /// Writes into appsettings.local.json under + /// Authentication:Local:SigningKey, merging into the existing JSON so unrelated + /// configuration keys are not overwritten. /// public void Persist(string signingKey) { diff --git a/Proxytrace.Api/Auth/AuthUserResolver.cs b/Proxytrace.Api/Auth/AuthUserResolver.cs index c46b155ed..1fbb5cb8f 100644 --- a/Proxytrace.Api/Auth/AuthUserResolver.cs +++ b/Proxytrace.Api/Auth/AuthUserResolver.cs @@ -24,7 +24,8 @@ public LocalUserResolver(IRepository users) } /// - /// Resolves. + /// Looks up the by the sub claim GUID. Fails the token validation + /// context and returns when the sub is unparseable or the user no longer exists. /// public async Task Resolve(TokenValidatedContext context, ClaimsPrincipal principal) { @@ -62,7 +63,8 @@ public JitUserResolver(IJitUserProvisioner provisioner, AuthOptions options) } /// - /// Resolves. + /// Derives an external subject identifier from the token's issuer and subject claims, then + /// JIT-provisions or retrieves the matching local user via IJitUserProvisioner. /// public async Task Resolve(TokenValidatedContext context, ClaimsPrincipal principal) { diff --git a/Proxytrace.Api/Auth/DataDirectorySigningKeyStore.cs b/Proxytrace.Api/Auth/DataDirectorySigningKeyStore.cs index d3ac8968f..43c62a2d3 100644 --- a/Proxytrace.Api/Auth/DataDirectorySigningKeyStore.cs +++ b/Proxytrace.Api/Auth/DataDirectorySigningKeyStore.cs @@ -22,7 +22,8 @@ public DataDirectorySigningKeyStore(string directory) } /// - /// Registers services with the Autofac container builder. + /// Reads the signing key from the signing-key file in the data directory, returning + /// when the file is absent or empty. /// public string? Load() { @@ -35,7 +36,8 @@ public DataDirectorySigningKeyStore(string directory) } /// - /// Persist. + /// Writes to the signing-key file in the data directory, + /// creating the directory if it does not exist. /// public void Persist(string signingKey) { diff --git a/Proxytrace.Api/Auth/IProjectAccessGuard.cs b/Proxytrace.Api/Auth/IProjectAccessGuard.cs index 24b4529a4..fabef317e 100644 --- a/Proxytrace.Api/Auth/IProjectAccessGuard.cs +++ b/Proxytrace.Api/Auth/IProjectAccessGuard.cs @@ -45,7 +45,8 @@ public ProjectAccessGuard( } /// - /// Determines whether the access project asynchronously. + /// Returns when the caller is an admin or a member of + /// , and the request's API key (if any) is confined to that project. /// public async Task CanAccessProjectAsync(Guid projectId, CancellationToken cancellationToken = default) { @@ -66,7 +67,9 @@ public async Task CanAccessProjectAsync(Guid projectId, CancellationToken } /// - /// Gets the accessible project ids asynchronously. + /// Returns the set of project ids the caller may see — for an admin who + /// may see all, an empty collection when the caller may see none, and the caller's member + /// projects otherwise. A REST API key further narrows the result to its single project. /// public async Task?> GetAccessibleProjectIdsAsync(CancellationToken cancellationToken = default) { diff --git a/Proxytrace.Api/Auth/JwtBearerEventsFactory.cs b/Proxytrace.Api/Auth/JwtBearerEventsFactory.cs index 4df306bc8..07763a061 100644 --- a/Proxytrace.Api/Auth/JwtBearerEventsFactory.cs +++ b/Proxytrace.Api/Auth/JwtBearerEventsFactory.cs @@ -11,7 +11,9 @@ internal static class JwtBearerEventsFactory private const string StreamTicketQueryKey = "stream_ticket"; /// - /// Creates. + /// Builds the that handle stream-ticket redemption, the SSE + /// ?access_token fallback, the httpOnly session-cookie fallback, and live role-claim + /// overwriting so a demoted user loses privileges on their next request rather than at token expiry. /// public static JwtBearerEvents Create() => new() { diff --git a/Proxytrace.Api/Auth/SessionCookie.cs b/Proxytrace.Api/Auth/SessionCookie.cs index e3f449176..85d24c05d 100644 --- a/Proxytrace.Api/Auth/SessionCookie.cs +++ b/Proxytrace.Api/Auth/SessionCookie.cs @@ -44,7 +44,7 @@ public interface ISessionCookie internal sealed class SessionCookie : ISessionCookie { /// - /// The name constant value. + /// The cookie name used for the local-mode session JWT (proxytrace_session). /// public const string Name = "proxytrace_session"; @@ -59,7 +59,9 @@ public SessionCookie(SessionCookieOptions options) } /// - /// Appends. + /// Sets the httpOnly session cookie on carrying , + /// expiring at . Applies SameSite=Strict and the configured + /// Secure flag. /// public void Append(HttpResponse response, string token, DateTimeOffset expiresAt) => response.Cookies.Append(Name, token, new CookieOptions @@ -72,7 +74,8 @@ public void Append(HttpResponse response, string token, DateTimeOffset expiresAt }); /// - /// Deletes. + /// Expires the session cookie on , effectively logging the user out + /// on the browser side. /// public void Delete(HttpResponse response) => response.Cookies.Delete(Name, new CookieOptions diff --git a/Proxytrace.Api/Auth/SigningKeyProvider.cs b/Proxytrace.Api/Auth/SigningKeyProvider.cs index 6f1f50338..d7c90a9fa 100644 --- a/Proxytrace.Api/Auth/SigningKeyProvider.cs +++ b/Proxytrace.Api/Auth/SigningKeyProvider.cs @@ -18,7 +18,10 @@ public SigningKeyProvider(ISigningKeyStore store) } /// - /// Ensures the signing key. + /// Returns the JWT signing key to use for local-mode authentication. Prefers + /// when set (must be at least 32 characters), then falls back to a + /// previously generated key from the store, and finally generates and persists a new key when none + /// exists. /// public string EnsureSigningKey(string? configured) { diff --git a/Proxytrace.Api/Configuration/SearchRequestOptions.cs b/Proxytrace.Api/Configuration/SearchRequestOptions.cs index dadc30823..091c2d3e4 100644 --- a/Proxytrace.Api/Configuration/SearchRequestOptions.cs +++ b/Proxytrace.Api/Configuration/SearchRequestOptions.cs @@ -6,24 +6,26 @@ namespace Proxytrace.Api.Configuration; public sealed record SearchRequestOptions { /// - /// Gets or sets the min query length. + /// Minimum number of characters a search query must contain before the request is accepted. /// public int MinQueryLength { get; init; } = 2; /// - /// Gets or sets the max query length. + /// Maximum number of characters a search query may contain; requests exceeding this are rejected + /// with a 400. /// public int MaxQueryLength { get; init; } = 200; /// - /// Gets or sets the min snippet length. + /// Minimum character length of a snippet submitted for snippet-search indexing. /// public int MinSnippetLength { get; init; } = 20; /// - /// Gets or sets the max snippet length. + /// Maximum character length of a snippet; snippets longer than this are rejected with a 400. /// public int MaxSnippetLength { get; init; } = 1000; /// - /// Validates. + /// Asserts that the configured bounds are internally consistent; throws + /// on startup when they are not. /// public void Validate() { diff --git a/Proxytrace.Api/Configuration/StatisticsOptions.cs b/Proxytrace.Api/Configuration/StatisticsOptions.cs index 14a18c9fc..f8ed7c103 100644 --- a/Proxytrace.Api/Configuration/StatisticsOptions.cs +++ b/Proxytrace.Api/Configuration/StatisticsOptions.cs @@ -13,19 +13,23 @@ public sealed record StatisticsOptions public const double DashboardPollIntervalSeconds = 30d; /// - /// Gets or sets the default recent trace count. + /// How many recent traces the dashboard statistics endpoint returns when the caller does not + /// specify a count. /// public int DefaultRecentTraceCount { get; init; } = 6; /// - /// Gets or sets the max recent trace count. + /// Hard cap on the number of recent traces the dashboard endpoint will return, regardless of what + /// the caller requests. /// public int MaxRecentTraceCount { get; init; } = 50; /// - /// Gets or sets the default agent limit. + /// How many agents the dashboard's agent-breakdown list returns when the caller does not specify + /// a limit. /// public int DefaultAgentLimit { get; init; } = 10; /// - /// Gets or sets the max agent limit. + /// Hard cap on the agent count the dashboard endpoint returns, regardless of what the caller + /// requests. /// public int MaxAgentLimit { get; init; } = 100; @@ -37,7 +41,8 @@ public sealed record StatisticsOptions public double DashboardCacheTtlSeconds { get; init; } = 10d; /// - /// Validates. + /// Asserts that the configured page sizes and cache TTL are internally consistent; throws + /// on startup when they are not. /// public void Validate() { From 988cb471fec8ae28f26fd7b633ac4892a591ec7e Mon Sep 17 00:00:00 2001 From: Claude Date: Sat, 15 Aug 2026 21:56:37 +0000 Subject: [PATCH 4/5] docs: improve docstrings in Api/Controllers + Application/Auth + Storage/Agent Replace auto-generated name-derived summaries with contextual descriptions reflecting actual endpoint semantics, scoping rules, and entity behaviour. Co-Authored-By: Claude Sonnet 4.6 Claude-Session: https://claude.ai/code/session_01NaEvHNpxKuuKG1k1SQw957 --- .../Controllers/AgentCallsController.cs | 26 ++++++-- .../Controllers/AgentsController.cs | 31 ++++++--- .../Controllers/AnomalyStreamController.cs | 4 +- .../ApplicationErrorsController.cs | 6 +- .../Controllers/AuditLogController.cs | 7 +- Proxytrace.Api/Controllers/AuthController.cs | 66 +++++++++++++------ .../Controllers/ConfigController.cs | 7 +- .../Controllers/CostLimitsController.cs | 18 +++-- .../CustomAnomalyDetectorsController.cs | 19 ++++-- .../Controllers/EmailSettingsController.cs | 11 ++-- .../Controllers/EvaluatorsController.cs | 37 +++++++---- Proxytrace.Application/Auth/AuthMode.cs | 9 ++- Proxytrace.Application/Auth/AuthOptions.cs | 29 ++++---- .../Internal/Entities/Agent/AgentEntity.cs | 12 ++-- .../Entities/Agent/AgentRepository.cs | 36 ++++++---- 15 files changed, 223 insertions(+), 95 deletions(-) diff --git a/Proxytrace.Api/Controllers/AgentCallsController.cs b/Proxytrace.Api/Controllers/AgentCallsController.cs index bb927cbaa..5b4508948 100644 --- a/Proxytrace.Api/Controllers/AgentCallsController.cs +++ b/Proxytrace.Api/Controllers/AgentCallsController.cs @@ -26,7 +26,9 @@ namespace Proxytrace.Api.Controllers; /// -/// API controller for agent calls operations. +/// CRUD and streaming endpoints for agent calls (traces). Provides filtered, paginated trace +/// listings; per-trace full detail; a histogram and KPI summary; a real-time SSE stream scoped +/// to the caller's projects; and test-support seed/delete operations. /// [ApiController] [Authorize] @@ -126,7 +128,9 @@ private static (Guid Id, string Key) DeriveSession(Guid projectId, string sessio } /// - /// Gets the all. + /// Returns a paginated, sorted list of agent calls matching the supplied filter. Scoped to the + /// caller's accessible projects; returns an empty page when the caller has no access or the + /// scope resolves to nothing. /// [HttpGet] public async Task> GetAll( @@ -238,7 +242,9 @@ public async Task> GetAllFull( } /// - /// Gets the overview. + /// Returns the traces-page overview: agents sorted by last-call time, per-agent call-count + /// breakdowns, and latency percentiles (P50/P95/P99). Scoped to the caller's accessible + /// projects. /// [HttpGet("overview")] public async Task GetOverview( @@ -280,7 +286,9 @@ public async Task GetOverview( } /// - /// Gets the histogram. + /// Returns a time-bucketed call-count histogram matching the same filters as + /// . The number of buckets can be specified (clamped to 1–240); each bucket + /// carries a start timestamp, total count, and error count. /// [HttpGet("histogram")] public async Task> GetHistogram( @@ -398,7 +406,8 @@ public async Task GetSummary( } /// - /// Gets. + /// Returns the full detail of a single agent call including request, response, tool calls, and + /// usage. Returns 404 when the call does not exist or the caller cannot access its project. /// [HttpGet("{id:guid}")] public async Task> Get(Guid id, CancellationToken cancellationToken) @@ -529,7 +538,9 @@ await sessionRepository.RecordActivityAsync( } /// - /// Stream. + /// SSE stream that emits trace-created events as new agent calls arrive. Non-admin callers + /// receive only traces belonging to their accessible projects; global (admin) callers receive + /// all. Sends periodic heartbeat comments to detect half-open sockets. /// [HttpGet("stream")] public async Task Stream(CancellationToken cancellationToken) @@ -565,7 +576,8 @@ public async Task Stream(CancellationToken cancellationToken) } /// - /// Deletes. + /// Permanently deletes a trace and reverses its contribution to the owning session's token + /// counters. Returns 404 when the trace does not exist or the caller cannot access its project. /// [HttpDelete("{id:guid}")] public async Task Delete(Guid id, CancellationToken cancellationToken) diff --git a/Proxytrace.Api/Controllers/AgentsController.cs b/Proxytrace.Api/Controllers/AgentsController.cs index 5411b04f0..c07e92caf 100644 --- a/Proxytrace.Api/Controllers/AgentsController.cs +++ b/Proxytrace.Api/Controllers/AgentsController.cs @@ -21,7 +21,9 @@ namespace Proxytrace.Api.Controllers; /// -/// API controller for agents operations. +/// CRUD and streaming endpoints for agents. Listing excludes archived (soft-deleted) agents; +/// deletion soft-deletes user agents and rejects system agents; SSE streams push proposal and +/// theory changes per agent. /// [ApiController] [Authorize] @@ -76,7 +78,9 @@ public AgentsController( } /// - /// Gets the all. + /// Returns a paginated list of non-archived agents visible to the caller, sorted by last-call + /// time then by updated-at descending. Scoped to the optional projectId or to the + /// caller's accessible projects when omitted. /// [HttpGet] public async Task> GetAll( @@ -115,7 +119,9 @@ public async Task> GetAll( } /// - /// Gets. + /// Returns the full detail of a single agent including its current prompt, tools, endpoint, and + /// last-call time. Returns 404 when the agent does not exist or the caller cannot access its + /// project. /// [HttpGet("{id:guid}")] public async Task> Get(Guid id, CancellationToken cancellationToken) @@ -166,7 +172,9 @@ public async Task> Seed( } /// - /// Stream proposals. + /// SSE stream that emits proposal-created and proposal-status-changed events for + /// the specified agent. Returns 404 when the agent does not exist or the caller cannot access its + /// project. /// [HttpGet("{id:guid}/proposals/stream")] public async Task StreamProposals(Guid id, CancellationToken cancellationToken) @@ -207,7 +215,8 @@ public async Task StreamProposals(Guid id, CancellationToken cancellationToken) } /// - /// Stream theories. + /// SSE stream that emits theory-changed events for the specified agent's optimization + /// theories. Returns 404 when the agent does not exist or the caller cannot access its project. /// [HttpGet("{id:guid}/theories/stream")] public async Task StreamTheories(Guid id, CancellationToken cancellationToken) @@ -242,7 +251,9 @@ public async Task StreamTheories(Guid id, CancellationToken cancellationToken) } /// - /// Deletes. + /// Soft-deletes (archives) a user agent, freeing its license slot while preserving its history. + /// Returns 404 when the agent does not exist or the caller cannot access its project, and 409 + /// when the agent is a system agent that must not be removed. /// [HttpDelete("{id:guid}")] public async Task Delete(Guid id, CancellationToken cancellationToken) @@ -269,7 +280,9 @@ public async Task Delete(Guid id, CancellationToken cancellationT } /// - /// Updates the endpoint. + /// Replaces the model endpoint an agent proxies through. Records an audit event only when the + /// endpoint actually changes. Returns 404 when the agent or the new endpoint does not exist or + /// the caller cannot access the agent's project. /// [HttpPatch("{id:guid}/endpoint")] public async Task UpdateEndpoint( @@ -293,7 +306,9 @@ public async Task UpdateEndpoint( } /// - /// Lists the versions. + /// Returns all prompt versions of an agent, sorted by version number descending. Each version + /// carries its system-prompt fingerprint so the UI can highlight which version is active. + /// Returns 404 when the agent does not exist or the caller cannot access its project. /// [HttpGet("{id:guid}/versions")] public async Task>> ListVersions( diff --git a/Proxytrace.Api/Controllers/AnomalyStreamController.cs b/Proxytrace.Api/Controllers/AnomalyStreamController.cs index 5aa7e65aa..a889d22f0 100644 --- a/Proxytrace.Api/Controllers/AnomalyStreamController.cs +++ b/Proxytrace.Api/Controllers/AnomalyStreamController.cs @@ -30,7 +30,9 @@ public AnomalyStreamController( } /// - /// Stream. + /// SSE stream that emits anomaly-flagged events as custom anomaly detectors flag calls. + /// Non-admin callers receive only events belonging to their accessible projects; periodic + /// heartbeat comments detect half-open sockets. /// [HttpGet("stream")] public async Task Stream(CancellationToken cancellationToken) diff --git a/Proxytrace.Api/Controllers/ApplicationErrorsController.cs b/Proxytrace.Api/Controllers/ApplicationErrorsController.cs index f7ff47ee6..89075f077 100644 --- a/Proxytrace.Api/Controllers/ApplicationErrorsController.cs +++ b/Proxytrace.Api/Controllers/ApplicationErrorsController.cs @@ -27,7 +27,8 @@ public ApplicationErrorsController(IApplicationErrorRepository repository) } /// - /// Gets the all. + /// Returns a paginated, newest-first list of captured application errors. Supports filtering by + /// severity level, free-text search, and date range. Admin-only. /// [HttpGet] public async Task> GetAll( @@ -44,7 +45,8 @@ public async Task> GetAll( } /// - /// Gets. + /// Returns the full detail of a single application error including its stack trace. Admin-only; + /// returns 404 when the error does not exist. /// [HttpGet("{id:guid}")] public async Task> Get(Guid id, CancellationToken cancellationToken) diff --git a/Proxytrace.Api/Controllers/AuditLogController.cs b/Proxytrace.Api/Controllers/AuditLogController.cs index 41e9fe7d8..70be17b4a 100644 --- a/Proxytrace.Api/Controllers/AuditLogController.cs +++ b/Proxytrace.Api/Controllers/AuditLogController.cs @@ -37,7 +37,9 @@ public AuditLogController( } /// - /// Gets the all. + /// Returns a paginated, newest-first list of audit log entries scoped to the caller's + /// accessible projects. Admins see every entry including global rows; project members see only + /// their projects' rows and never global entries. /// [HttpGet] public async Task>> GetAll( @@ -90,7 +92,8 @@ public async Task>> GetAll( } /// - /// Gets. + /// Returns a single audit log entry. Returns 404 when it does not exist or the caller cannot + /// access its project (existence is never disclosed to unauthorized callers). /// [HttpGet("{id:guid}")] public async Task> Get(Guid id, CancellationToken cancellationToken) diff --git a/Proxytrace.Api/Controllers/AuthController.cs b/Proxytrace.Api/Controllers/AuthController.cs index 38568eb78..04ea3f65c 100644 --- a/Proxytrace.Api/Controllers/AuthController.cs +++ b/Proxytrace.Api/Controllers/AuthController.cs @@ -16,7 +16,9 @@ namespace Proxytrace.Api.Controllers; /// -/// API controller for auth operations. +/// Authentication endpoints for local-mode (password + TOTP MFA) and OIDC mode. Covers login, +/// logout, invite-based signup, password reset, MFA enrollment/verification, stream-ticket +/// issuance, and the /me session-restore endpoint. /// [ApiController] [Route("api/auth")] @@ -76,7 +78,9 @@ public AuthController( } /// - /// Gets the mode. + /// Returns the authentication mode (local or oidc), whether first-admin setup is + /// still required, and whether a legacy-account claim is available. Anonymous; used by the SPA + /// before any session exists. /// [HttpGet("mode")] [AllowAnonymous] @@ -89,7 +93,9 @@ public async Task GetMode(CancellationToken ct) } /// - /// Claim legacy. + /// Claims the single unclaimed legacy (pre-auth) admin account by associating it with a new + /// email and password. Local-mode only; rate-limited per IP. Returns 409 when no eligible + /// legacy account exists. /// [HttpPost("claim-legacy")] [AllowAnonymous] @@ -108,7 +114,8 @@ public async Task> ClaimLegacy([FromBody] ClaimLegac } /// - /// Setup. + /// Creates the first admin account. Local-mode only; returns 409 after setup has already been + /// completed. Issues a session immediately so the wizard can continue authenticated. /// [HttpPost("setup")] [AllowAnonymous] @@ -131,7 +138,8 @@ public async Task> Setup([FromBody] SetupAdminReques // leak via browser history / Referer / proxy logs). Validate this ticket in // JwtBearerEventsFactory.OnMessageReceived alongside the existing access_token path. /// - /// Stream ticket. + /// Issues a short-lived, single-use ticket for authenticating SSE connections without leaking + /// the long-lived session JWT in a query string. Requires an active session. /// [HttpGet("stream-ticket")] [Authorize] @@ -147,7 +155,9 @@ public async Task> StreamTicket(CancellationT // Rate-limited per client IP: there is no per-account failed-attempt counter, so without this // password guessing against a known address is bounded only by request throughput. /// - /// Login. + /// Authenticates with email and password. Local-mode only; rate-limited per IP. Returns 401 on + /// bad credentials. When MFA is required a challenge token is returned instead of a session; + /// the session is issued after the second factor is verified at mfa/verify. /// [HttpPost("login")] [AllowAnonymous] @@ -190,7 +200,8 @@ private LoginResponseDto IssueLoginResponse(LoginOutcome outcome) // Completes the second step of login: verifies a TOTP code (or backup code) against the challenge // ticket and, on success, issues the session. Rate-limited because the TOTP code space is small. /// - /// Mfa verify. + /// Completes the second step of login by verifying a TOTP code (or backup code) against the + /// challenge ticket and issuing a session. Local-mode only; rate-limited per IP. /// [HttpPost("mfa/verify")] [AllowAnonymous] @@ -213,7 +224,9 @@ public async Task> MfaVerify([FromBody] MfaVerify // Starts TOTP enrollment: returns a fresh secret + otpauth URI for the caller to add to their // authenticator app. The enrollment is pending until confirmed via mfa/activate. /// - /// Mfa setup. + /// Begins TOTP enrollment for the authenticated user, returning a TOTP secret and + /// otpauth:// URI to add to an authenticator app. Enrollment is not active until + /// confirmed via mfa/activate. Returns 409 when MFA is already enabled. /// [HttpPost("mfa/setup")] [Authorize] @@ -230,7 +243,8 @@ public async Task> MfaSetup(CancellationToken ct) // Confirms enrollment with a first code, turning MFA on and returning one-time backup codes (shown once). /// - /// Mfa activate. + /// Confirms TOTP enrollment with a first code, enabling MFA on the account and returning one-time + /// backup codes that are shown only at this point. Returns 400 when the code is invalid. /// [HttpPost("mfa/activate")] [Authorize] @@ -249,7 +263,8 @@ public async Task> MfaActivate([FromBody] MfaA // Self-service disable: requires the account password as re-authentication. /// - /// Mfa disable. + /// Self-service MFA disable: verifies the account password as re-authentication before removing + /// the TOTP enrollment. Returns 400 when the password is incorrect. Local-mode only. /// [HttpPost("mfa/disable")] [Authorize] @@ -268,7 +283,8 @@ public async Task MfaDisable([FromBody] MfaDisableRequest req, Ca // The session rides in an httpOnly cookie (see SessionCookie), so the SPA cannot read // or clear it itself — logout clears it server-side. Anonymous and idempotent. /// - /// Logout. + /// Clears the httpOnly session cookie server-side, logging the user out from the browser. No-op + /// when no session cookie is present. Local-mode only; anonymous and idempotent. /// [HttpPost("logout")] [AllowAnonymous] @@ -287,7 +303,8 @@ public IActionResult Logout() // Session restore for the SPA: identifies the caller from the httpOnly session cookie // (or bearer token), since the client cannot decode the cookie itself. /// - /// Me. + /// Returns the authenticated caller's user record, role, notification preferences, MFA status, + /// and whether email is configured. Used by the SPA to restore session state after a page reload. /// [HttpGet("me")] [Authorize] @@ -305,7 +322,9 @@ public async Task> Me(CancellationToken ct) } /// - /// Signup. + /// Consumes an invite token to create a new user account and immediately issues a session. + /// Local-mode only; rate-limited per IP. Returns 410 when the invite is invalid, expired, or + /// already consumed. /// [HttpPost("signup")] [AllowAnonymous] @@ -331,7 +350,9 @@ public async Task> Signup([FromBody] SignupRequest r // the reset link is emailed; otherwise it is written to the server log for the operator to relay // (the only escape from a sole-admin lockout). Rate-limited to blunt enumeration/abuse. /// - /// Forgot password. + /// Initiates a password-reset flow for the given email. Always returns 202 regardless of whether + /// the email matches an account, to prevent address enumeration. The reset link is emailed when + /// SMTP is configured, otherwise logged for operator relay. Rate-limited per IP. /// [HttpPost("forgot-password")] [AllowAnonymous] @@ -345,7 +366,9 @@ public async Task ForgotPassword([FromBody] ForgotPasswordRequest } /// - /// Reset password. + /// Completes a password-reset flow by validating the one-time token and setting the new + /// password. Returns 410 when the token is invalid or already used. On success either issues a + /// session directly (no MFA) or returns an MFA challenge token when the account has TOTP enabled. /// [HttpPost("reset-password")] [AllowAnonymous] @@ -384,7 +407,9 @@ private string BuildResetUrl(string token) // Anonymous lookup of an invite by its raw token — rate-limited so the token space cannot be // swept, sharing the login bucket because both are anonymous credential guesses. /// - /// Preview. + /// Returns the email, role, and expiry of an invite by its raw token — used by the signup page + /// to pre-fill the form. Anonymous; rate-limited per IP. Returns 410 when the invite is invalid + /// or already consumed. /// [HttpGet("invites/by-token/{token}")] [AllowAnonymous] @@ -398,7 +423,8 @@ public async Task> Preview(string token, Cancella } /// - /// Creates. + /// Creates a user invite for the given email and role. Admin-only; local-mode only. Returns the + /// one-time invite URL (the raw token is not recoverable from the list endpoint). /// [Authorize(Roles = nameof(UserRole.Admin))] [RequireLocalMode] @@ -426,7 +452,8 @@ private string BuildInviteUrl(string token) } /// - /// Lists. + /// Returns all invites with their status (pending, consumed, expired). Admin-only; local-mode + /// only. The invite link is not shown here — it is returned once when the invite is created. /// [Authorize(Roles = nameof(UserRole.Admin))] [RequireLocalMode] @@ -440,7 +467,8 @@ public async Task> List(CancellationToken ct) } /// - /// Deletes. + /// Revokes a pending invite so its token can no longer be used to sign up. Admin-only; local-mode + /// only. Returns 404 when the invite does not exist. /// [Authorize(Roles = nameof(UserRole.Admin))] [RequireLocalMode] diff --git a/Proxytrace.Api/Controllers/ConfigController.cs b/Proxytrace.Api/Controllers/ConfigController.cs index dc20f2bbe..5c2ca69fb 100644 --- a/Proxytrace.Api/Controllers/ConfigController.cs +++ b/Proxytrace.Api/Controllers/ConfigController.cs @@ -6,7 +6,9 @@ namespace Proxytrace.Api.Controllers; /// -/// API controller for config operations. +/// Anonymous endpoint that surfaces the runtime configuration the SPA needs before any user exists: +/// kiosk mode flag, interactive mode flag, application version, and the public ingestion proxy base +/// URL. /// [ApiController] [Route("api/config")] @@ -33,7 +35,8 @@ public ConfigController( } /// - /// Gets. + /// Returns the anonymous configuration payload used by the SPA on startup: kiosk mode, whether + /// interactive features are available, the application version, and the ingestion proxy base URL. /// [HttpGet] [AllowAnonymous] diff --git a/Proxytrace.Api/Controllers/CostLimitsController.cs b/Proxytrace.Api/Controllers/CostLimitsController.cs index 2d2d02624..9eb1ea4d6 100644 --- a/Proxytrace.Api/Controllers/CostLimitsController.cs +++ b/Proxytrace.Api/Controllers/CostLimitsController.cs @@ -68,7 +68,8 @@ public CostLimitsController( } /// - /// Gets the all. + /// Returns all cost budgets for the given project. Returns an empty list (not 404) when the + /// caller cannot access the project. /// [HttpGet] public async Task> GetAll( @@ -118,7 +119,8 @@ public async Task> GetStatus( } /// - /// Gets. + /// Returns a single cost budget by id. Returns 404 when it does not exist or the caller cannot + /// access its project. /// [HttpGet("{id:guid}")] public async Task> Get(Guid id, CancellationToken cancellationToken) @@ -133,7 +135,9 @@ public async Task> Get(Guid id, CancellationToken can } /// - /// Creates. + /// Creates a monthly cost budget scoped to a project, agent, or API key. Admin-only; requires + /// the CostControls license feature. Returns 409 when a budget with the same scope + /// already exists, and 400 when thresholds are invalid. /// [HttpPost] [Authorize(Roles = nameof(UserRole.Admin))] @@ -205,7 +209,9 @@ public async Task> Create( } /// - /// Updates. + /// Updates the soft/hard limit amounts and enabled state for a budget. Clears any existing + /// breach records so the budget is re-armed after a threshold change. Admin-only; requires the + /// CostControls license feature. Returns 404 when the budget does not exist. /// [HttpPut("{id:guid}")] [Authorize(Roles = nameof(UserRole.Admin))] @@ -248,7 +254,9 @@ public async Task> Update( } /// - /// Deletes. + /// Deletes a cost budget and clears its breach records, lifting any active hard-block on the + /// proxy. Admin-only; requires the CostControls license feature. Returns 404 when the + /// budget does not exist. /// [HttpDelete("{id:guid}")] [Authorize(Roles = nameof(UserRole.Admin))] diff --git a/Proxytrace.Api/Controllers/CustomAnomalyDetectorsController.cs b/Proxytrace.Api/Controllers/CustomAnomalyDetectorsController.cs index 95ba763b5..5b7f67f76 100644 --- a/Proxytrace.Api/Controllers/CustomAnomalyDetectorsController.cs +++ b/Proxytrace.Api/Controllers/CustomAnomalyDetectorsController.cs @@ -72,7 +72,9 @@ public CustomAnomalyDetectorsController( } /// - /// Gets the all. + /// Returns all custom anomaly detectors for the given project. Returns an empty list (not 404) + /// when the caller cannot access the project. Requires the CustomAnomalyDetectors license + /// feature. /// [HttpGet] public async Task> GetAll( @@ -88,7 +90,8 @@ public async Task> GetAll( } /// - /// Gets. + /// Returns a single custom anomaly detector by id. Returns 404 when it does not exist or the + /// caller cannot access its project. /// [HttpGet("{id:guid}")] public async Task> Get(Guid id, CancellationToken cancellationToken) @@ -102,7 +105,10 @@ public async Task> Get(Guid id, Cancellat } /// - /// Creates. + /// Creates a new LLM-based anomaly detector and its hidden system agent. Each detector carries + /// a set of regex/literal triggers, a review-instructions system prompt, a judge model endpoint, + /// and an optional per-agent scope. Returns 400 when the endpoint or any agent id is not found, + /// or when the trigger patterns are invalid. /// [HttpPost] public async Task> Create( @@ -161,7 +167,9 @@ public async Task> Create( } /// - /// Updates. + /// Replaces all mutable fields of a custom anomaly detector. If the endpoint changes, the hidden + /// agent is updated; new instructions create a new prompt version on the hidden agent. Returns + /// 404 when the detector does not exist or the caller cannot access its project. /// [HttpPut("{id:guid}")] public async Task> Update( @@ -216,7 +224,8 @@ public async Task> Update( } /// - /// Deletes. + /// Deletes a custom anomaly detector and its hidden system agent in a single transaction. + /// Returns 404 when the detector does not exist or the caller cannot access its project. /// [HttpDelete("{id:guid}")] public async Task Delete(Guid id, CancellationToken cancellationToken) diff --git a/Proxytrace.Api/Controllers/EmailSettingsController.cs b/Proxytrace.Api/Controllers/EmailSettingsController.cs index 73983407b..b7178d08b 100644 --- a/Proxytrace.Api/Controllers/EmailSettingsController.cs +++ b/Proxytrace.Api/Controllers/EmailSettingsController.cs @@ -11,7 +11,8 @@ namespace Proxytrace.Api.Controllers; /// -/// API controller for email settings operations. +/// Admin-only SMTP configuration endpoints. Supports reading, updating, and test-sending the +/// email settings used for notifications, invites, and password resets. /// [ApiController] [Authorize(Roles = nameof(UserRole.Admin))] @@ -48,7 +49,7 @@ public EmailSettingsController( } /// - /// Gets. + /// Returns the current SMTP email configuration, or 204 when none has been saved yet. /// [HttpGet] public async Task> Get(CancellationToken cancellationToken) @@ -58,7 +59,8 @@ public async Task> Get(CancellationToken cancella } /// - /// Updates. + /// Saves a new SMTP configuration. An empty password in the request preserves the existing stored + /// password; supply a new value to rotate it. /// [HttpPut] public async Task> Update( @@ -79,7 +81,8 @@ public async Task> Update( } /// - /// Sends the test. + /// Sends a test email to the authenticated admin's address using the current SMTP configuration. + /// Returns 400 with a message when email is not configured or the send fails. /// [HttpPost("test")] public async Task SendTest(CancellationToken cancellationToken) diff --git a/Proxytrace.Api/Controllers/EvaluatorsController.cs b/Proxytrace.Api/Controllers/EvaluatorsController.cs index 70479c48c..f2c30c665 100644 --- a/Proxytrace.Api/Controllers/EvaluatorsController.cs +++ b/Proxytrace.Api/Controllers/EvaluatorsController.cs @@ -19,7 +19,9 @@ namespace Proxytrace.Api.Controllers; /// -/// API controller for evaluators operations. +/// CRUD and read endpoints for evaluators. Supports rule-based, LLM, and agentic evaluator kinds; +/// an overview combining evaluators, suites, and per-evaluator sparklines; and a per-evaluator +/// detail view with recent evaluation results. /// [ApiController] [Authorize] @@ -104,7 +106,8 @@ private async Task> ListScopedSuitesAsync( } /// - /// Gets the agentic presets. + /// Returns the built-in agentic-evaluator presets (name, key, system-prompt) available to + /// pre-populate a new agentic evaluator. /// [HttpGet("agentic-presets")] public IReadOnlyList GetAgenticPresets() @@ -113,7 +116,8 @@ public IReadOnlyList GetAgenticPresets() .ToArray(); /// - /// Gets the all. + /// Returns the full detail of all evaluators visible to the caller, optionally scoped to a + /// project. Scoped to the caller's accessible projects when no project filter is given. /// [HttpGet] public async Task> GetAll( @@ -126,7 +130,8 @@ public async Task> GetAll( } /// - /// Gets the summaries. + /// Returns lightweight summaries (id, kind, name) of all evaluators visible to the caller — + /// used to populate evaluator-picker drop-downs without fetching full detail. /// [HttpGet("summaries")] public async Task> GetSummaries( @@ -139,7 +144,8 @@ public async Task> GetSummaries( } /// - /// Gets. + /// Returns the full detail of a single evaluator. Returns 404 when it does not exist or the + /// caller cannot access its project. /// [HttpGet("{id:guid}")] public async Task> Get(Guid id, CancellationToken cancellationToken) @@ -153,7 +159,8 @@ public async Task> Get(Guid id, CancellationTok } /// - /// Gets the overview. + /// Returns the evaluators overview: all evaluators, their test suites, and optional time-bucketed + /// pass-rate sparklines per evaluator scoped to the caller's accessible projects. /// [HttpGet("overview")] public async Task GetOverview( @@ -192,7 +199,9 @@ public async Task GetOverview( } /// - /// Gets the detail view. + /// Returns the evaluator detail view: aggregate pass-rate statistics bucketed by time, plus the + /// most recent evaluation results with their linked test-run ids. Requires from and + /// to query parameters; returns 400 when either is missing. /// [HttpGet("{id:guid}/detail")] public async Task> GetDetailView( @@ -228,7 +237,8 @@ public async Task> GetDetailView( } /// - /// Creates. + /// Creates a new evaluator (rule-based, LLM-judge, or agentic) for the specified project. + /// Returns 400 when the project is not found and 404 when the caller cannot access it. /// [HttpPost] public async Task> Create( @@ -259,7 +269,8 @@ public async Task> Create( } /// - /// Updates. + /// Replaces all mutable fields of an evaluator. Returns 404 when it does not exist or the + /// caller cannot access its project. /// [HttpPut("{id:guid}")] public async Task> Update( @@ -288,7 +299,9 @@ public async Task> Update( } /// - /// Deletes. + /// Soft-deletes (archives) an evaluator, detaching it from test suites while preserving + /// historical test results. Returns 404 when it does not exist or the caller cannot access its + /// project. /// [HttpDelete("{id:guid}")] public async Task Delete(Guid id, CancellationToken cancellationToken) @@ -311,7 +324,9 @@ public async Task Delete(Guid id, CancellationToken cancellationT } /// - /// Recent evaluations. + /// Returns the most recent evaluation results for an evaluator, optionally filtered by score, + /// each annotated with its test-run id. Returns 404 when the evaluator does not exist or the + /// caller cannot access its project. /// [HttpGet("{id:guid}/recent-evaluations")] public async Task>> RecentEvaluations( diff --git a/Proxytrace.Application/Auth/AuthMode.cs b/Proxytrace.Application/Auth/AuthMode.cs index e037a01b8..36e0d3b37 100644 --- a/Proxytrace.Application/Auth/AuthMode.cs +++ b/Proxytrace.Application/Auth/AuthMode.cs @@ -1,10 +1,17 @@ namespace Proxytrace.Application.Auth; /// -/// Specifies the auth mode. +/// Selects between local password-based authentication and OIDC-backed authentication. /// public enum AuthMode { + /// + /// Authentication is handled by an external OIDC provider configured via . + /// Oidc, + + /// + /// Authentication is handled by the built-in local password store and JWT issuer. + /// Local, } diff --git a/Proxytrace.Application/Auth/AuthOptions.cs b/Proxytrace.Application/Auth/AuthOptions.cs index 61ee83e79..2ed4dd32c 100644 --- a/Proxytrace.Application/Auth/AuthOptions.cs +++ b/Proxytrace.Application/Auth/AuthOptions.cs @@ -1,16 +1,17 @@ namespace Proxytrace.Application.Auth; /// -/// Represents a auth options. +/// Root configuration block for authentication, bound from the 'Authentication' config section. /// public sealed class AuthOptions { /// - /// Gets or sets the oidc. + /// OIDC configuration block; a non-empty activates OIDC mode. /// public OidcOptions Oidc { get; init; } = new(); + /// - /// Gets or sets the local. + /// Local auth configuration block, used when no OIDC authority is configured. /// public LocalSection Local { get; init; } = new(); @@ -25,7 +26,7 @@ public sealed class AuthOptions public bool EmergencyLogResetLink { get; init; } /// - /// Provides additional functionality. + /// Computed auth mode: when an OIDC authority is configured, otherwise. /// public AuthMode Mode => string.IsNullOrWhiteSpace(Oidc.Authority) @@ -33,39 +34,43 @@ public AuthMode Mode : AuthMode.Oidc; /// - /// Represents a oidc options. + /// OIDC-provider settings consumed by the API's JWT bearer middleware. /// public sealed class OidcOptions { /// - /// Gets or sets the authority. + /// OIDC authority URL; a non-empty value enables OIDC mode and is used for metadata discovery. /// public string Authority { get; init; } = string.Empty; + /// - /// Gets or sets the audience. + /// Expected audience claim used to validate incoming OIDC tokens. /// public string Audience { get; init; } = string.Empty; + /// - /// Gets or sets the require https metadata. + /// Whether the OIDC metadata endpoint must use HTTPS; set to false only in local development. /// public bool RequireHttpsMetadata { get; init; } = true; + /// - /// Gets or sets the email claim type. + /// Claim type the OIDC provider uses for the user's email address. /// public string EmailClaimType { get; init; } = "email"; + /// - /// Gets or sets the name claim type. + /// Claim type the OIDC provider uses for the user's display name. /// public string NameClaimType { get; init; } = "name"; } /// - /// Represents a local section. + /// Local-auth sub-section that mirrors for hosts that bind the top-level 'Authentication' block. /// public sealed class LocalSection { /// - /// Gets or sets the signing key. + /// Hex-encoded HMAC-SHA256 key used to sign local session JWTs. /// public string SigningKey { get; init; } = string.Empty; } diff --git a/Proxytrace.Storage/Internal/Entities/Agent/AgentEntity.cs b/Proxytrace.Storage/Internal/Entities/Agent/AgentEntity.cs index 30b1bf5c1..4b9694112 100644 --- a/Proxytrace.Storage/Internal/Entities/Agent/AgentEntity.cs +++ b/Proxytrace.Storage/Internal/Entities/Agent/AgentEntity.cs @@ -10,23 +10,25 @@ internal record SystemPromptData(string Name, string Template); internal record AgentEntity : Entity, IArchivableEntity { /// - /// Gets or sets the name. + /// Human-readable display name of the agent, unique within the owning project. Maps to the agents.name column. /// public required string Name { get; init; } /// - /// Gets or sets the project. + /// FK to the project that owns this agent. Maps to the agents.project_id column. /// public required Guid Project { get; init; } /// - /// Gets or sets the endpoint. + /// FK to the model endpoint this agent routes calls through by default. Maps to the agents.endpoint_id column. /// public required Guid Endpoint { get; init; } /// - /// Gets or sets the is system agent. + /// True for agents created automatically by the proxy to represent a recognized configuration, not by a user. + /// System agents are excluded from the licensed agent count and are filtered out of most user-facing lists. /// public required bool IsSystemAgent { get; init; } /// - /// Gets or sets the model parameters. + /// JSON-serialized inference parameters (temperature, top-p, etc.) applied to calls through this agent. + /// Maps to the agents.model_parameters column. /// public required ModelParametersData ModelParameters { get; init; } diff --git a/Proxytrace.Storage/Internal/Entities/Agent/AgentRepository.cs b/Proxytrace.Storage/Internal/Entities/Agent/AgentRepository.cs index 1b3d4cab4..2915986e7 100644 --- a/Proxytrace.Storage/Internal/Entities/Agent/AgentRepository.cs +++ b/Proxytrace.Storage/Internal/Entities/Agent/AgentRepository.cs @@ -31,7 +31,10 @@ internal class AgentRepository : ArchivableRepository, IAge private readonly IEntityCache? versionCache; /// - /// Initializes a new instance of the class. + /// Initializes the repository with all collaborators required for agent persistence: the base + /// mapper/context/event infrastructure, fingerprinting services for deduplication, a name + /// generator for auto-named agents, an async lock to serialize concurrent GetOrCreate races, and + /// optional caches for agents and versions. /// public AgentRepository( IMapper mapper, @@ -62,7 +65,9 @@ public AgentRepository( } /// - /// Upsert asynchronously. + /// Persists an agent update if the agent already exists, or creates the agent together with its + /// initial version if it does not. Delegates to PersistWithInitialVersionAsync for new agents + /// to satisfy the storage invariant that every agent row has a corresponding version row. /// public override async Task UpsertAsync(IAgent entity, CancellationToken cancellationToken = default) { @@ -74,7 +79,10 @@ public override async Task UpsertAsync(IAgent entity, CancellationToken } /// - /// Gets the or create asynchronously. + /// Returns the agent whose current version matches the given system prompt and tool set within the project, + /// creating a new agent and version if no match exists. Uses an in-process async lock keyed by strict + /// fingerprint to serialize concurrent creation races; a DB-level unique constraint on the fingerprint + /// handles the remaining multi-instance race. /// public async Task GetOrCreateAsync( IPromptTemplate systemPrompt, @@ -135,7 +143,8 @@ public override async Task AddAsync(IAgent entity, CancellationToken can } /// - /// Creates the with initial version asynchronously. + /// Creates a new agent with the given name, system prompt, tools, and endpoint, persisting the agent + /// and its initial version in a single transaction to satisfy the storage invariant. /// public Task CreateWithInitialVersionAsync( string name, @@ -215,25 +224,28 @@ private Task SetCurrentVersionIdAsync(Guid agentId, Guid versionId, Cancellation }); /// - /// Gets the agent fingerprint. + /// Computes the strict fingerprint (SHA-256 of system prompt plus sorted tools including descriptions) + /// for the given system prompt and tool set. Used as the deduplication key in GetOrCreate races. /// public string GetAgentFingerprint(IPromptTemplate systemPrompt, IReadOnlyCollection tools) => fingerprinter.Strict(systemPrompt, tools); /// - /// Gets the agent fingerprint. + /// Computes the strict fingerprint for the given agent's current system prompt and tool set. /// public string GetAgentFingerprint(IAgent agent) => GetAgentFingerprint(agent.SystemPrompt, agent.Tools); /// - /// Sets the current version asynchronously. + /// Updates the CurrentVersionId column of the agent row to point to the given version, also advancing + /// UpdatedAt. Invalidates both the agent cache entry and the version cache. /// public Task SetCurrentVersionAsync(Guid agentId, Guid versionId, CancellationToken cancellationToken = default) => SetCurrentVersionIdAsync(agentId, versionId, cancellationToken); /// - /// Counts the non system asynchronously. + /// Returns the total number of non-system, non-archived agents across all projects. Used by the + /// licensing layer to enforce the per-tier agent count limit. /// public async Task CountNonSystemAsync(CancellationToken cancellationToken = default) => await contextFactory() @@ -243,7 +255,8 @@ public async Task CountNonSystemAsync(CancellationToken cancellationToken = .CountAsync(e => !e.IsSystemAgent && !e.IsArchived, cancellationToken); /// - /// Finds the by name asynchronously. + /// Returns the agent in the given project with the exact display name, or null if no match exists. + /// Name uniqueness within a project is enforced by a database constraint. /// public async Task FindByNameAsync(IProject project, string name, CancellationToken cancellationToken = default) { @@ -260,7 +273,7 @@ public async Task CountNonSystemAsync(CancellationToken cancellationToken = } /// - /// Gets the by project asynchronously. + /// Returns all non-archived agents belonging to the given project, in no particular order. /// public async Task> GetByProjectAsync(Guid projectId, CancellationToken cancellationToken = default) { @@ -275,7 +288,8 @@ public async Task> GetByProjectAsync(Guid projectId, Cance } /// - /// Gets the project id asynchronously. + /// Returns the project ID for the given agent, or null if the agent does not exist. + /// Projects only the FK column — no full entity mapping. /// public async Task GetProjectIdAsync(Guid agentId, CancellationToken cancellationToken = default) => await contextFactory() From 306a487212dce7d66b78b189dae1e1496b8d5599 Mon Sep 17 00:00:00 2001 From: Claude Date: Sun, 16 Aug 2026 05:48:43 +0000 Subject: [PATCH 5/5] docs: improve docstrings with code-understanding-based summaries (partial) Replace auto-generated name-derived docstrings with contextual summaries that describe actual behaviour from reading the implementation. Covers 31 files across Application, Domain, and Storage layers; remaining files will follow in subsequent commits. Co-Authored-By: Claude Sonnet 4.6 Claude-Session: https://claude.ai/code/session_01NaEvHNpxKuuKG1k1SQw957 --- .../Auth/IJitUserProvisioner.cs | 7 ++- .../Auth/Local/IInviteService.cs | 19 ++++-- .../Auth/Local/ILegacyClaimService.cs | 8 ++- .../Auth/Local/ILocalTokenIssuer.cs | 23 ++++--- .../Auth/Local/ILoginService.cs | 2 +- .../Auth/Local/IPasswordPolicy.cs | 12 ++-- .../Auth/Local/IPasswordService.cs | 9 ++- .../Cleanup/AgentCallCleanupConfiguration.cs | 7 ++- .../Cleanup/IDataCleanupService.cs | 5 +- .../Evaluator/IAgenticEvaluatorPresets.cs | 7 ++- Proxytrace.Application/Module.cs | 2 +- .../Optimization/IOptimizer.cs | 2 +- .../Optimization/IOptimizerService.cs | 2 +- .../Playground/IPlaygroundService.cs | 5 +- .../Search/SearchConfiguration.cs | 16 +++-- Proxytrace.Application/Setup/ISetupService.cs | 25 ++++++-- .../Statistics/DashboardCacheOptions.cs | 2 +- .../Statistics/IAgentStatistics.cs | 3 + .../Statistics/IDashboardStatistics.cs | 6 ++ Proxytrace.Domain/IProjectSpecific.cs | 6 +- Proxytrace.Domain/Module.cs | 6 +- Proxytrace.Domain/Proposal/IProposal.cs | 9 ++- Proxytrace.Domain/Proposal/Priority.cs | 10 ++- Proxytrace.Domain/Search/ISearchIndexer.cs | 11 +++- Proxytrace.Domain/Search/ISearchService.cs | 15 ++++- Proxytrace.Domain/Search/ISearchable.cs | 4 +- Proxytrace.Domain/Search/SearchHit.cs | 9 ++- .../Entities/AgentCall/AgentCallEntity.cs | 63 ++++++++++++------- .../Entities/AgentCall/AgentCallRepository.cs | 46 ++++++++++---- .../AgentVersion/AgentVersionEntity.cs | 11 ++-- .../AgentVersion/AgentVersionRepository.cs | 7 ++- 31 files changed, 264 insertions(+), 95 deletions(-) diff --git a/Proxytrace.Application/Auth/IJitUserProvisioner.cs b/Proxytrace.Application/Auth/IJitUserProvisioner.cs index 0e27710e5..7236e0b87 100644 --- a/Proxytrace.Application/Auth/IJitUserProvisioner.cs +++ b/Proxytrace.Application/Auth/IJitUserProvisioner.cs @@ -3,12 +3,15 @@ namespace Proxytrace.Application.Auth; /// -/// Represents a jit user provisioner. +/// Creates or returns the local user record for an OIDC subject on first login, handling the just-in-time provisioning flow. /// public interface IJitUserProvisioner { + /// + /// Returns the user for the given OIDC subject, creating a local record on first login. + /// Task EnsureProvisionedAsync( string externalSubject, - string email, + string email, CancellationToken cancellationToken = default); } diff --git a/Proxytrace.Application/Auth/Local/IInviteService.cs b/Proxytrace.Application/Auth/Local/IInviteService.cs index adfa41d04..09efdca96 100644 --- a/Proxytrace.Application/Auth/Local/IInviteService.cs +++ b/Proxytrace.Application/Auth/Local/IInviteService.cs @@ -10,22 +10,31 @@ namespace Proxytrace.Application.Auth.Local; public sealed record InviteCreated(IInvite Invite, string RawToken); /// -/// Service that provides invite functionality. +/// Manages email invitations: creates invite records with hashed tokens, validates tokens on redemption, and creates the invited user on first use. /// public interface IInviteService { + /// + /// Creates an invite for the given email and returns the entity with its raw (un-hashed) token for building the invite link. + /// Task CreateAsync( string email, UserRole role, IUser invitedBy, CancellationToken cancellationToken = default); - + + /// + /// Returns the active invite matching the given raw token, or null when the token is unknown or expired. + /// Task GetByTokenAsync( string token, CancellationToken cancellationToken = default); - + + /// + /// Redeems the invite token, creates the user account with the supplied password, and returns the new user; null when the token is invalid. + /// Task ConsumeAsync( - string token, - string password, + string token, + string password, CancellationToken cancellationToken = default); } diff --git a/Proxytrace.Application/Auth/Local/ILegacyClaimService.cs b/Proxytrace.Application/Auth/Local/ILegacyClaimService.cs index 690b6bf71..da1fb9e5e 100644 --- a/Proxytrace.Application/Auth/Local/ILegacyClaimService.cs +++ b/Proxytrace.Application/Auth/Local/ILegacyClaimService.cs @@ -1,12 +1,18 @@ namespace Proxytrace.Application.Auth.Local; /// -/// Service that provides legacy claim functionality. +/// Handles the one-time first-admin claim flow for installations bootstrapped with an auto-generated token instead of the setup wizard. /// public interface ILegacyClaimService { + /// + /// Returns true when the installation has an unclaimed bootstrap token and no admin has been set up yet. + /// Task IsClaimAvailableAsync(CancellationToken cancellationToken = default); + /// + /// Validates the bootstrap token, creates the first admin account, and returns a session token; null when the token is invalid or already claimed. + /// Task ClaimAsync( string email, string password, diff --git a/Proxytrace.Application/Auth/Local/ILocalTokenIssuer.cs b/Proxytrace.Application/Auth/Local/ILocalTokenIssuer.cs index c987fb9c5..e530a7bbe 100644 --- a/Proxytrace.Application/Auth/Local/ILocalTokenIssuer.cs +++ b/Proxytrace.Application/Auth/Local/ILocalTokenIssuer.cs @@ -3,41 +3,48 @@ namespace Proxytrace.Application.Auth.Local; /// -/// Represents a local token issuer. +/// Issues signed JWT session tokens for locally-authenticated users. /// public interface ILocalTokenIssuer { + /// + /// Mints a signed JWT for the given user, returning the token string and its expiry. + /// LocalTokenResult Issue(IUser user); } /// -/// Encapsulates the result of a local token operation. +/// A freshly minted JWT and its expiry timestamp. /// public sealed record LocalTokenResult(string Token, DateTimeOffset ExpiresAt); /// -/// Represents a local auth options. +/// Configuration for the local JWT issuer, bound from the 'Authentication:Local' config section. /// public sealed class LocalAuthOptions { /// - /// The section name constant value. + /// Configuration section path where is bound. /// public const string SectionName = "Authentication:Local"; + /// - /// Gets or sets the signing key. + /// Hex-encoded HMAC-SHA256 key used to sign local session JWTs. /// public string SigningKey { get; init; } = string.Empty; + /// - /// Gets or sets the issuer. + /// JWT 'iss' claim value; identifies this Proxytrace installation as the token issuer. /// public string Issuer { get; init; } = "proxytrace-local"; + /// - /// Gets or sets the audience. + /// JWT 'aud' claim value; must match the bearer validation audience configured in the API. /// public string Audience { get; init; } = "proxytrace-api"; + /// - /// Gets or sets the token lifetime. + /// How long issued tokens remain valid before the user must re-authenticate. /// public TimeSpan TokenLifetime { get; init; } = TimeSpan.FromDays(7); } diff --git a/Proxytrace.Application/Auth/Local/ILoginService.cs b/Proxytrace.Application/Auth/Local/ILoginService.cs index 3de6aadb5..6647166f6 100644 --- a/Proxytrace.Application/Auth/Local/ILoginService.cs +++ b/Proxytrace.Application/Auth/Local/ILoginService.cs @@ -3,7 +3,7 @@ namespace Proxytrace.Application.Auth.Local; /// -/// Service that provides login functionality. +/// Authenticates a user by email and password and issues a session token, handling the MFA challenge when the account requires a second factor. /// public interface ILoginService { diff --git a/Proxytrace.Application/Auth/Local/IPasswordPolicy.cs b/Proxytrace.Application/Auth/Local/IPasswordPolicy.cs index f936c818c..17d68a98b 100644 --- a/Proxytrace.Application/Auth/Local/IPasswordPolicy.cs +++ b/Proxytrace.Application/Auth/Local/IPasswordPolicy.cs @@ -1,26 +1,30 @@ namespace Proxytrace.Application.Auth.Local; /// -/// Represents a password policy. +/// Validates a candidate password against the installation's strength requirements before it is hashed and stored. /// public interface IPasswordPolicy { + /// + /// Returns a result indicating whether the password satisfies all policy rules, with one error message per violated rule. + /// PasswordValidationResult Validate(string password); } /// -/// Encapsulates the result of a password validation operation. +/// Outcome of a password policy check: a pass/fail flag plus the list of violated rule descriptions. /// public sealed record PasswordValidationResult( bool IsValid, IReadOnlyList Errors) { /// - /// Ok. + /// Returns a passing result with no errors. /// public static PasswordValidationResult Ok() => new(true, []); + /// - /// Fail. + /// Returns a failing result carrying the supplied rule-violation messages. /// public static PasswordValidationResult Fail(params string[] errors) => new(false, errors); } diff --git a/Proxytrace.Application/Auth/Local/IPasswordService.cs b/Proxytrace.Application/Auth/Local/IPasswordService.cs index a3494404b..309cf103e 100644 --- a/Proxytrace.Application/Auth/Local/IPasswordService.cs +++ b/Proxytrace.Application/Auth/Local/IPasswordService.cs @@ -3,11 +3,18 @@ namespace Proxytrace.Application.Auth.Local; /// -/// Service that provides password functionality. +/// Hashes passwords for storage and verifies them on login using PBKDF2. /// public interface IPasswordService { + /// + /// Computes the PBKDF2 hash of the given password, salted per user, for storage. + /// string Hash(IUser user, string password); + + /// + /// Returns true when the supplied plain-text password matches the stored hash. + /// bool Verify(IUser user, string hash, string password); /// diff --git a/Proxytrace.Application/Cleanup/AgentCallCleanupConfiguration.cs b/Proxytrace.Application/Cleanup/AgentCallCleanupConfiguration.cs index 6bfc2045f..d34f1b974 100644 --- a/Proxytrace.Application/Cleanup/AgentCallCleanupConfiguration.cs +++ b/Proxytrace.Application/Cleanup/AgentCallCleanupConfiguration.cs @@ -1,16 +1,17 @@ namespace Proxytrace.Application.Cleanup; /// -/// Configuration for agent call cleanup. +/// Retention settings for the agent-call (trace) history, bound from the 'AgentCallCleanup' config section. /// public sealed record AgentCallCleanupConfiguration { /// - /// Gets or sets the retention duration days. + /// Traces older than this are permanently deleted on each cleanup pass. /// public int RetentionDurationDays { get; init; } = 30; + /// - /// Gets or sets the cleanup interval hours. + /// How often the background cleanup service scans for and removes expired traces. /// public int CleanupIntervalHours { get; init; } = 6; } diff --git a/Proxytrace.Application/Cleanup/IDataCleanupService.cs b/Proxytrace.Application/Cleanup/IDataCleanupService.cs index 725636e74..b164df2f1 100644 --- a/Proxytrace.Application/Cleanup/IDataCleanupService.cs +++ b/Proxytrace.Application/Cleanup/IDataCleanupService.cs @@ -1,9 +1,12 @@ namespace Proxytrace.Application.Cleanup; /// -/// Service that provides data cleanup functionality. +/// Bulk-resets a project's operational data for demo resets and test teardown. /// public interface IDataCleanupService { + /// + /// Deletes all traces, test runs, statistics, and notifications while preserving model configuration (agents, endpoints, providers, suites). + /// Task DeleteAllNonModelDataAsync(CancellationToken cancellationToken = default); } diff --git a/Proxytrace.Application/Evaluator/IAgenticEvaluatorPresets.cs b/Proxytrace.Application/Evaluator/IAgenticEvaluatorPresets.cs index e5169efe3..83102862c 100644 --- a/Proxytrace.Application/Evaluator/IAgenticEvaluatorPresets.cs +++ b/Proxytrace.Application/Evaluator/IAgenticEvaluatorPresets.cs @@ -1,14 +1,17 @@ namespace Proxytrace.Application.Evaluator; /// -/// Represents a agentic evaluator presets. +/// Provides the built-in judge prompts that are auto-provisioned as agentic evaluators for every new project. /// public interface IAgenticEvaluatorPresets { + /// + /// Returns all registered presets, each of which is seeded as a default evaluator on project creation. + /// IReadOnlyList GetAll(); } /// -/// Represents a agentic evaluator preset. +/// A single built-in evaluator template: its stable key, display name, and the judge-LLM system prompt. /// public sealed record AgenticEvaluatorPreset(string Key, string Name, string SystemPrompt); diff --git a/Proxytrace.Application/Module.cs b/Proxytrace.Application/Module.cs index 129b8697b..6d4ca5475 100644 --- a/Proxytrace.Application/Module.cs +++ b/Proxytrace.Application/Module.cs @@ -54,7 +54,7 @@ namespace Proxytrace.Application; /// -/// Autofac module that registers services. +/// Root Autofac module for the Application layer; registers all background services, hosted workers, streaming broadcasters, and cross-cutting infrastructure. /// public sealed class Module : Autofac.Module { diff --git a/Proxytrace.Application/Optimization/IOptimizer.cs b/Proxytrace.Application/Optimization/IOptimizer.cs index 1b8dca70d..63f282fa8 100644 --- a/Proxytrace.Application/Optimization/IOptimizer.cs +++ b/Proxytrace.Application/Optimization/IOptimizer.cs @@ -4,7 +4,7 @@ namespace Proxytrace.Application.Optimization; /// -/// Represents a optimizer. +/// Analyses a completed test-run group and proposes optimization theories for underperforming agents. /// public interface IOptimizer { diff --git a/Proxytrace.Application/Optimization/IOptimizerService.cs b/Proxytrace.Application/Optimization/IOptimizerService.cs index 2ed74d5fc..936eaf878 100644 --- a/Proxytrace.Application/Optimization/IOptimizerService.cs +++ b/Proxytrace.Application/Optimization/IOptimizerService.cs @@ -3,7 +3,7 @@ namespace Proxytrace.Application.Optimization; /// -/// Service that provides optimizer functionality. +/// Background queue that feeds completed test-run groups through the optimizer pipeline and broadcasts any discovered theories. /// public interface IOptimizerService { diff --git a/Proxytrace.Application/Playground/IPlaygroundService.cs b/Proxytrace.Application/Playground/IPlaygroundService.cs index 85e3000a5..10f9e4f9d 100644 --- a/Proxytrace.Application/Playground/IPlaygroundService.cs +++ b/Proxytrace.Application/Playground/IPlaygroundService.cs @@ -3,10 +3,13 @@ namespace Proxytrace.Application.Playground; /// -/// Service that provides playground functionality. +/// Executes a one-off prompt against a chosen agent and endpoint, streaming tokens back as they arrive. /// public interface IPlaygroundService { + /// + /// Streams the model's response tokens for the given playground request until generation completes or cancellation is requested. + /// IAsyncEnumerable CompleteStreamAsync( PlaygroundCompleteRequest request, CancellationToken cancellationToken); diff --git a/Proxytrace.Application/Search/SearchConfiguration.cs b/Proxytrace.Application/Search/SearchConfiguration.cs index a6d53b356..786edd77f 100644 --- a/Proxytrace.Application/Search/SearchConfiguration.cs +++ b/Proxytrace.Application/Search/SearchConfiguration.cs @@ -1,28 +1,32 @@ namespace Proxytrace.Application.Search; /// -/// Configuration for search. +/// Configuration for the Lucene-backed full-text search index, bound from the 'Search' config section. /// public sealed record SearchConfiguration { /// - /// Gets or sets the index path. + /// Filesystem directory where the Lucene index segments are written. /// public string IndexPath { get; init; } = "searchindex"; + /// - /// Gets or sets the trace retention days. + /// Trace entries older than this are pruned from the search index independently of DB retention. /// public int TraceRetentionDays { get; init; } = 30; + /// - /// Gets or sets the pruner interval hours. + /// How often the background pruner runs to remove stale trace index entries. /// public int PrunerIntervalHours { get; init; } = 6; + /// - /// Gets or sets the hits per kind. + /// Maximum results returned per entity kind (traces, agents, suites) in a single search response. /// public int HitsPerKind { get; init; } = 5; + /// - /// Gets or sets the snippet max chars. + /// Maximum character length of the highlighted text snippet attached to each search hit. /// public int SnippetMaxChars { get; init; } = 160; } diff --git a/Proxytrace.Application/Setup/ISetupService.cs b/Proxytrace.Application/Setup/ISetupService.cs index bba233e7b..9558e955a 100644 --- a/Proxytrace.Application/Setup/ISetupService.cs +++ b/Proxytrace.Application/Setup/ISetupService.cs @@ -3,23 +3,38 @@ namespace Proxytrace.Application.Setup; /// -/// Service that provides setup functionality. +/// Drives the first-run setup wizard: validates a provider connection, creates the initial provider/endpoint/project/admin-user, and gates further use until setup completes. /// public interface ISetupService { + /// + /// Runs the full first-run setup: creates the provider, endpoint, project, and default agent, then provisions the first admin user. + /// Task CompleteAsync(SetupInput input, CancellationToken cancellationToken = default); + /// + /// Validates provider credentials by listing models without persisting any state. + /// Task TestProviderConnectionAsync(ProviderConnectionInput input, CancellationToken cancellationToken = default); + /// + /// Returns the model names available at the given provider endpoint, used to populate the setup wizard's model picker. + /// Task> ListProviderModelsAsync(ProviderConnectionInput input, CancellationToken cancellationToken = default); + /// + /// Creates the initial admin account and returns a session token; callable only before any users exist. + /// Task CreateFirstAdminAsync(string email, string password, CancellationToken cancellationToken = default); + /// + /// Returns true when at least one user account exists, used to determine whether first-run setup has already completed. + /// Task AnyUsersExistAsync(CancellationToken cancellationToken = default); } /// -/// Represents a provider connection input. +/// Parameters for testing or creating a model provider connection during setup. /// public record ProviderConnectionInput( string ProviderName, @@ -28,7 +43,7 @@ public record ProviderConnectionInput( ModelProviderKind ProviderKind); /// -/// Represents a setup input. +/// Full first-run setup payload: provider details, an initial model, and the first project name. /// public record SetupInput( string ProviderName, @@ -39,7 +54,7 @@ public record SetupInput( string ProjectName); /// -/// Encapsulates the result of a setup operation. +/// Identifiers for the provider, endpoint, and project created by the setup wizard. /// public record SetupResult( Guid ProviderId, @@ -47,6 +62,6 @@ public record SetupResult( Guid ProjectId); /// -/// Encapsulates the result of a first admin operation. +/// Session credentials for the first admin account created during setup. /// public record FirstAdminResult(Guid UserId, string Token, DateTimeOffset ExpiresAt); diff --git a/Proxytrace.Application/Statistics/DashboardCacheOptions.cs b/Proxytrace.Application/Statistics/DashboardCacheOptions.cs index 24b20135b..67fe305ad 100644 --- a/Proxytrace.Application/Statistics/DashboardCacheOptions.cs +++ b/Proxytrace.Application/Statistics/DashboardCacheOptions.cs @@ -17,7 +17,7 @@ public sealed record DashboardCacheOptions public double TtlSeconds { get; init; } = 10d; /// - /// Gets the ttl. + /// expressed as a for consumers that take a duration directly. /// public TimeSpan Ttl => TimeSpan.FromSeconds(TtlSeconds); } diff --git a/Proxytrace.Application/Statistics/IAgentStatistics.cs b/Proxytrace.Application/Statistics/IAgentStatistics.cs index c40365e79..c960198da 100644 --- a/Proxytrace.Application/Statistics/IAgentStatistics.cs +++ b/Proxytrace.Application/Statistics/IAgentStatistics.cs @@ -6,6 +6,9 @@ namespace Proxytrace.Application.Statistics; /// public interface IAgentStatistics { + /// + /// Returns aggregated call count, pass rate, latency, and cost totals for the agent over the requested time window and bucket granularity. + /// Task GetAgentOverviewAsync(Guid agentId, DateTimeOffset from, DateTimeOffset to, StatisticsBucket bucket, CancellationToken cancellationToken = default); /// diff --git a/Proxytrace.Application/Statistics/IDashboardStatistics.cs b/Proxytrace.Application/Statistics/IDashboardStatistics.cs index cc3888152..d27ff8489 100644 --- a/Proxytrace.Application/Statistics/IDashboardStatistics.cs +++ b/Proxytrace.Application/Statistics/IDashboardStatistics.cs @@ -13,8 +13,14 @@ public interface IDashboardStatistics /// Task GetDashboardViewAsync(StatisticsFilter filter, int recentTraceCount, int agentLimit, CancellationToken cancellationToken = default); + /// + /// Returns per-agent call volume, error rate, and cost totals for the filter window, used to populate the agents table on the dashboard. + /// Task> GetAgentBreakdownAsync(StatisticsFilter filter, CancellationToken cancellationToken = default); + /// + /// Returns bucketed p50/p95 latency series for the filter window, used to draw the latency chart. + /// Task> GetLatencyAsync(StatisticsFilter filter, CancellationToken cancellationToken = default); /// diff --git a/Proxytrace.Domain/IProjectSpecific.cs b/Proxytrace.Domain/IProjectSpecific.cs index dd7aa9530..cea78ff85 100644 --- a/Proxytrace.Domain/IProjectSpecific.cs +++ b/Proxytrace.Domain/IProjectSpecific.cs @@ -3,9 +3,11 @@ namespace Proxytrace.Domain; /// -/// Something that has a relation to a +/// Marks an entity as belonging to a specific , enabling project-scoped +/// authorization checks, search indexing, and cost attribution throughout the domain. /// -public interface IProjectSpecific +public interface IProjectSpecific { + /// The project that owns this entity. IProject Project { get; } } diff --git a/Proxytrace.Domain/Module.cs b/Proxytrace.Domain/Module.cs index 5e0b2cebb..1a1720d9d 100644 --- a/Proxytrace.Domain/Module.cs +++ b/Proxytrace.Domain/Module.cs @@ -17,14 +17,16 @@ namespace Proxytrace.Domain; /// -/// Autofac module that registers services. +/// Autofac module that wires up domain services: factory delegates for every entity, the DI-friendly +/// evaluator and proposal generators, the agent-version fingerprinter, the prompt-template repository, +/// and the two-phase construction that hides the shell + initial-version seam. /// public sealed class Module : Autofac.Module { private const string RegisteredKey = "Proxytrace.Domain.Module.Registered"; /// - /// Adds Proxytrace domain services. + /// Registers all Proxytrace domain services into the Autofac container. /// protected override void Load(ContainerBuilder builder) { diff --git a/Proxytrace.Domain/Proposal/IProposal.cs b/Proxytrace.Domain/Proposal/IProposal.cs index a6d62dbbf..95d360bbd 100644 --- a/Proxytrace.Domain/Proposal/IProposal.cs +++ b/Proxytrace.Domain/Proposal/IProposal.cs @@ -1,12 +1,15 @@ namespace Proxytrace.Domain.Proposal; /// -/// Represents a proposal. +/// Common base for actionable optimization proposals surfaced to users, covering system-prompt, +/// tool-list, and model-switch changes. Each proposal carries a priority and a human-readable +/// rationale derived from the evidence that motivated it. /// public interface IProposal : IDomainEntity { - - + /// How urgently the proposed change should be adopted. Priority Priority { get; } + + /// Human-readable description of what this proposal recommends and why. string Description { get; } } diff --git a/Proxytrace.Domain/Proposal/Priority.cs b/Proxytrace.Domain/Proposal/Priority.cs index 087293d1b..5b33d4e85 100644 --- a/Proxytrace.Domain/Proposal/Priority.cs +++ b/Proxytrace.Domain/Proposal/Priority.cs @@ -1,12 +1,20 @@ namespace Proxytrace.Domain.Proposal; /// -/// Specifies the priority. +/// Relative urgency of an optimization theory or proposal, used to surface the most impactful +/// recommendations first in the UI and to set expectations about how quickly a change should be adopted. /// public enum Priority { + /// Minor improvement; adopt when convenient. Low = 0, + + /// Meaningful improvement; should be reviewed in the near term. Medium = 1, + + /// Significant gain in quality or efficiency; prioritize for the next review cycle. High = 2, + + /// Severe regression or large opportunity; requires immediate attention. Critical = 3, } diff --git a/Proxytrace.Domain/Search/ISearchIndexer.cs b/Proxytrace.Domain/Search/ISearchIndexer.cs index 2ff3c1372..077600d7a 100644 --- a/Proxytrace.Domain/Search/ISearchIndexer.cs +++ b/Proxytrace.Domain/Search/ISearchIndexer.cs @@ -1,12 +1,21 @@ namespace Proxytrace.Domain.Search; /// -/// Represents a search indexer. +/// Write side of the search index: adds, removes, and bulk-reindexes documents, flushing buffered +/// writes to the backing store. Called by the application layer after entity mutations and by the +/// admin reindex trigger. /// public interface ISearchIndexer { + /// Indexes or re-indexes a single entity of the given kind in the given project. Task IndexAsync(SearchKind kind, Guid projectId, Guid entityId, CancellationToken cancellationToken = default); + + /// Removes the document for the given entity from the index. Task RemoveAsync(SearchKind kind, Guid entityId, CancellationToken cancellationToken = default); + + /// Deletes and rebuilds the full index for the given project from the database. Task ReindexProjectAsync(Guid projectId, CancellationToken cancellationToken = default); + + /// Flushes any buffered write operations to the backing search store. Task FlushAsync(CancellationToken cancellationToken = default); } diff --git a/Proxytrace.Domain/Search/ISearchService.cs b/Proxytrace.Domain/Search/ISearchService.cs index 0fb4b1bde..a08366f92 100644 --- a/Proxytrace.Domain/Search/ISearchService.cs +++ b/Proxytrace.Domain/Search/ISearchService.cs @@ -1,12 +1,21 @@ namespace Proxytrace.Domain.Search; /// -/// Service that provides search functionality. +/// Full-text search over indexed project entities. Provides ranked query results, entity-id lookups +/// scoped to a single kind (for selective retrieval), and a recency feed used by the search UI's +/// "recent" suggestions. /// public interface ISearchService { + /// + /// Returns ranked search hits across all indexed kinds for the given project and query string. + /// Task SearchAsync(Guid projectId, string query, CancellationToken cancellationToken = default); + /// + /// Returns up to entity ids of the given + /// matching the query, used when only ids (not full hit payloads) are needed. + /// Task> SearchEntityIdsAsync( Guid projectId, string query, @@ -14,6 +23,10 @@ Task> SearchEntityIdsAsync( int maxHits, CancellationToken cancellationToken = default); + /// + /// Returns the most recently indexed documents of the given kinds, + /// used to populate "recent" suggestions before the user types a query. + /// Task GetRecentAsync( Guid projectId, IReadOnlyList kinds, diff --git a/Proxytrace.Domain/Search/ISearchable.cs b/Proxytrace.Domain/Search/ISearchable.cs index 746650e50..790699eab 100644 --- a/Proxytrace.Domain/Search/ISearchable.cs +++ b/Proxytrace.Domain/Search/ISearchable.cs @@ -1,9 +1,11 @@ namespace Proxytrace.Domain.Search; /// -/// Represents a searchable. +/// Marks an entity as participating in the full-text search index, declaring which +/// bucket it occupies so the indexer can route it to the correct document type. /// public interface ISearchable : IProjectSpecific { + /// The search index bucket this entity occupies (e.g. ). SearchKind SearchKind { get; } } diff --git a/Proxytrace.Domain/Search/SearchHit.cs b/Proxytrace.Domain/Search/SearchHit.cs index 63c036f4d..d41178405 100644 --- a/Proxytrace.Domain/Search/SearchHit.cs +++ b/Proxytrace.Domain/Search/SearchHit.cs @@ -1,12 +1,19 @@ namespace Proxytrace.Domain.Search; /// -/// Represents a search hit. +/// A single ranked result returned by a full-text search query, carrying enough data for the UI to +/// render a result card and navigate to the entity without a second round-trip. /// public sealed record SearchHit( + /// The entity type this result belongs to (agent, test suite, etc.). SearchKind Kind, + /// Id of the matched entity. Guid EntityId, + /// Display title of the matched entity (e.g. agent or suite name). string Title, + /// Excerpt from the indexed text showing the match context, truncated to the configured snippet length. string Snippet, + /// Relevance score from the search engine; higher is more relevant. double Score, + /// Kind-specific key/value annotations (e.g. project id, status) used for display or routing. IReadOnlyDictionary Metadata); diff --git a/Proxytrace.Storage/Internal/Entities/AgentCall/AgentCallEntity.cs b/Proxytrace.Storage/Internal/Entities/AgentCall/AgentCallEntity.cs index 77ebe010f..2342a44d4 100644 --- a/Proxytrace.Storage/Internal/Entities/AgentCall/AgentCallEntity.cs +++ b/Proxytrace.Storage/Internal/Entities/AgentCall/AgentCallEntity.cs @@ -8,59 +8,72 @@ namespace Proxytrace.Storage.Internal.Entities.AgentCall; internal record AgentCallEntity : Entity { /// - /// Gets or sets the agent version id. + /// FK to the agent version active when this call was made. Joins to AgentVersionEntity to resolve + /// the agent and project. Maps to the agent_calls.agent_version_id column. /// public required Guid AgentVersionId { get; init; } /// - /// Gets or sets the endpoint id. + /// FK to the model endpoint the request was routed to (provider + model + pricing). Maps to + /// the agent_calls.endpoint_id column. /// public required Guid EndpointId { get; init; } /// - /// Gets or sets the request. + /// The full inbound conversation (system message plus user turns) serialized as JSON. This is the + /// authoritative request payload; RequestPreview is a denormalized excerpt for list queries. /// public required Conversation Request { get; init; } /// - /// Gets or sets the response. + /// The assistant response message serialized as JSON, or null when the call errored before + /// the provider returned a response. /// public required AssistantMessage? Response { get; init; } /// - /// Gets or sets the input tokens. + /// Number of non-cached prompt tokens billed by the provider, or null when the provider did + /// not return usage data (e.g. streaming errors). /// public required ulong? InputTokens { get; init; } /// - /// Gets or sets the output tokens. + /// Number of completion tokens generated by the provider, or null when usage was not reported. /// public required ulong? OutputTokens { get; init; } /// - /// Gets or sets the cached input tokens. + /// Number of prompt tokens served from the provider's prompt cache, or null when caching is + /// unsupported or usage was not reported. /// public required ulong? CachedInputTokens { get; init; } /// - /// Gets or sets the latency ms. + /// End-to-end round-trip time in milliseconds measured at the proxy, or null when the call + /// errored before a response arrived. /// public required double? LatencyMs { get; init; } /// - /// Gets or sets the http status. + /// HTTP status code returned to the caller — 2xx for success, 4xx/5xx for provider or proxy errors. + /// Used by the error-rate aggregation and the HTTP status filter. /// public required int HttpStatus { get; init; } /// - /// Gets or sets the finish reason. + /// The provider's finish_reason string (e.g. "stop", "tool_calls", "length"), or null when the + /// call did not complete successfully. /// public required string? FinishReason { get; init; } /// - /// Gets or sets the error message. + /// Human-readable error description when the call failed at the proxy or provider layer, null + /// on success. Stored for display in the trace detail view. /// public required string? ErrorMessage { get; init; } /// - /// Gets or sets the model parameters. + /// JSON-serialized inference parameters (temperature, top-p, etc.) that were in effect for this + /// call, captured at ingestion time. Maps to the agent_calls.model_parameters column. /// public required ModelParametersData ModelParameters { get; init; } /// - /// Gets or sets the conversation id. + /// Groups this call with others in the same logical conversation thread. Nullable and FK-free — + /// revocation of a conversation never cascades away telemetry. /// public required Guid? ConversationId { get; init; } /// - /// Gets or sets the session id. + /// Groups this call with others originating from the same proxy session (identified by the + /// caller-supplied external key). Nullable and FK-free — session deletion never cascades away telemetry. /// public required Guid? SessionId { get; init; } @@ -69,43 +82,51 @@ internal record AgentCallEntity : Entity // FK-free — like SessionId/ConversationId — so revoking a key never cascades away telemetry. // Backs the per-key cost breakdown and key-scoped budgets. /// - /// Gets or sets the api key id. + /// The Proxytrace API key this call authenticated with, or null when unauthenticated via key or + /// when the row predates key attribution. FK-free so key revocation never removes telemetry. /// public Guid? ApiKeyId { get; init; } // Outlier characteristics flagged at ingestion (bitmask). 0 = not an outlier. Persisted as a // single byte; a partial index (see AgentCallConfig) serves the "outliers only" trace filter. /// - /// Gets or sets the outlier flags. + /// Bitmask of anomaly characteristics detected at ingestion time (high latency, high token count, + /// error, etc.). Zero means no outlier flags. A partial index covers non-zero values for the + /// outliers-only trace filter. /// public OutlierFlags OutlierFlags { get; init; } // Denormalised summaries populated at write time so the traces-list query can project scalar // columns only, without reading/deserialising the Request and Response payload columns. /// - /// Gets or sets the request preview. + /// Truncated first user message from the request, populated at write time so list queries can + /// display a preview without deserializing the full Request JSON payload. /// public string? RequestPreview { get; init; } /// - /// Gets or sets the response tool request count. + /// Number of tool-call requests contained in the assistant response, denormalized at write time + /// for the ToolCount sort and display in the traces list. /// public int ResponseToolRequestCount { get; init; } // Denormalised at write time so token/cache sorts and range filters hit plain indexed columns // instead of per-row expressions. Null when the call reported no usage. /// - /// Gets or sets the total tokens. + /// Sum of InputTokens and OutputTokens, denormalized at write time so the total-token sort and + /// min/max token filters execute as indexed column comparisons rather than computed expressions. /// public ulong? TotalTokens { get; init; } /// - /// Gets or sets the cache hit rate. + /// Fraction of input tokens served from the provider's cache (CachedInputTokens / InputTokens), + /// denormalized at write time to allow indexed sorting by cache efficiency in the traces list. /// public double? CacheHitRate { get; init; } // One row per distinct tool name requested in the response — backs the ToolName filter's EXISTS // semi-join and the project-scoped tool-name picker. See AgentCallToolEntity/AgentCallToolConfig. /// - /// Gets or sets the tools. + /// Child rows indexing the distinct tool names present in the response, one row per name. Backs + /// the ToolName filter (EXISTS semi-join) and the project-scoped tool-name picker. /// public List Tools { get; init; } = []; } diff --git a/Proxytrace.Storage/Internal/Entities/AgentCall/AgentCallRepository.cs b/Proxytrace.Storage/Internal/Entities/AgentCall/AgentCallRepository.cs index 271dd14a4..5636aeb4e 100644 --- a/Proxytrace.Storage/Internal/Entities/AgentCall/AgentCallRepository.cs +++ b/Proxytrace.Storage/Internal/Entities/AgentCall/AgentCallRepository.cs @@ -30,7 +30,8 @@ internal class AgentCallRepository : AbstractRepository endpoints; /// - /// Initializes a new instance of the class. + /// Initializes the repository with the base infrastructure plus references to the version, agent, + /// and endpoint repositories used to resolve metadata for list projections and query filters. /// public AgentCallRepository( IMapper mapper, @@ -50,7 +51,9 @@ public AgentCallRepository( } /// - /// Gets the filtered asynchronously. + /// Returns a paged set of fully-hydrated agent calls matching the given filter, together with the + /// total count. Applies all filter predicates (project, agent, endpoint, date range, status, tokens, + /// latency, outlier flags, tool name, fulltext) before paging. /// public async Task<(IReadOnlyList Items, int Total)> GetFilteredAsync( AgentCallFilter filter, @@ -77,7 +80,9 @@ public AgentCallRepository( } /// - /// Gets the filtered list asynchronously. + /// Returns a paged list of lightweight trace summaries matching the given filter, together with the + /// total count. Projects scalar columns only — the large Request/Response JSON payloads are never + /// read. Agent and endpoint metadata is batch-resolved from the cached entity repositories. /// public async Task<(IReadOnlyList Items, int Total)> GetFilteredListAsync( AgentCallFilter filter, @@ -193,7 +198,9 @@ private sealed record ListRow( OutlierFlags OutlierFlags); /// - /// Gets the histogram asynchronously. + /// Returns a time-bucketed histogram of call counts and error counts for the given filter window. + /// Aggregates entirely in the database — one row per non-empty bucket — then expands to the + /// requested bucket count with zero-filled gaps in the domain layer. /// public async Task> GetHistogramAsync( AgentCallFilter filter, @@ -261,7 +268,9 @@ public async Task> GetHistogramAsync( } /// - /// Gets the summary asynchronously. + /// Returns aggregated statistics (total count, token usage, latency moments, error count, and cost) + /// for all calls matching the given filter. Groups by endpoint so cost can be priced per endpoint; + /// executes as a single grouped aggregate query. /// public async Task GetSummaryAsync( AgentCallFilter filter, @@ -534,7 +543,8 @@ private static IOrderedQueryable OrderNullable( : query.OrderBy(isNull).ThenBy(key).ThenBy(e => e.Id); /// - /// Gets the last call times asynchronously. + /// Returns a dictionary mapping each agent ID to the timestamp of its most recent call across + /// all versions. Executes as a single grouped aggregate query joining calls to agent versions. /// public async Task> GetLastCallTimesAsync( CancellationToken cancellationToken = default) @@ -553,7 +563,9 @@ into g } /// - /// Gets the last call time asynchronously. + /// Returns the timestamp of the most recent call for the given agent across all its versions, or + /// null when the agent has no recorded calls. Filters to this agent's version IDs in SQL so the + /// query stays indexed rather than scanning the full trace table. /// public async Task GetLastCallTimeAsync( Guid agentId, @@ -578,7 +590,9 @@ into g } /// - /// Finds the latest by conversation id asynchronously. + /// Returns the most recent call belonging to the given conversation within the given project, or + /// null if no matching call exists. Scoped to the project via an agent-version subquery to prevent + /// cross-project leakage. /// public async Task FindLatestByConversationIdAsync( Guid conversationId, @@ -603,7 +617,9 @@ into g } /// - /// Removes the older than asynchronously. + /// Deletes all agent calls created on or before the cutoff date. Executes as a server-side DELETE + /// on relational providers to avoid materializing rows; falls back to load-then-remove on the + /// in-memory provider (tests/kiosk) and also loads the Tools navigation to cascade child rows. /// public async Task RemoveOlderThanAsync(DateTimeOffset cutoffDate, CancellationToken cancellationToken = default) { @@ -629,7 +645,9 @@ public async Task RemoveOlderThanAsync(DateTimeOffset cutoffDate, Cancellat } /// - /// Gets the session removals older than asynchronously. + /// Returns the session-scoped trace and token deltas for all calls created on or before the + /// cutoff, grouped by session. Used by the retention sweep to decrement session counters before + /// deleting the calls. /// public async Task> GetSessionRemovalsOlderThanAsync( DateTimeOffset cutoffDate, @@ -665,7 +683,9 @@ public async Task> GetSessionRemovalsOlderTha } /// - /// Sets the outlier flag asynchronously. + /// Bitwise-ORs the given flag into the OutlierFlags column of the specified call, without + /// overwriting existing flags. Uses a server-side ExecuteUpdate on relational providers to avoid + /// a read-modify-write race between concurrent statistical writers. /// public async Task SetOutlierFlagAsync(Guid id, OutlierFlags flag, CancellationToken cancellationToken = default) { @@ -693,7 +713,9 @@ await query.ExecuteUpdateAsync( } /// - /// Gets the tool names asynchronously. + /// Returns the distinct tool names used in calls belonging to the given project, optionally + /// scoped to a single agent. Uses the denormalized AgentCallToolEntity rows so the query runs + /// as an indexed DISTINCT on the (ProjectId, AgentId, ToolName) index without touching the trace table. /// public async Task> GetToolNamesAsync( Guid projectId, Guid? agentId = null, CancellationToken cancellationToken = default) diff --git a/Proxytrace.Storage/Internal/Entities/AgentVersion/AgentVersionEntity.cs b/Proxytrace.Storage/Internal/Entities/AgentVersion/AgentVersionEntity.cs index 6bd40f811..8cf93bf67 100644 --- a/Proxytrace.Storage/Internal/Entities/AgentVersion/AgentVersionEntity.cs +++ b/Proxytrace.Storage/Internal/Entities/AgentVersion/AgentVersionEntity.cs @@ -9,7 +9,7 @@ namespace Proxytrace.Storage.Internal.Entities.AgentVersion; internal record AgentVersionEntity : Entity { /// - /// Gets or sets the agent id. + /// FK to the parent agent that owns this version. Maps to the agent_versions.agent_id column. /// public required Guid AgentId { get; init; } @@ -19,17 +19,20 @@ internal record AgentVersionEntity : Entity public required Guid Project { get; init; } /// - /// Gets or sets the version number. + /// Monotonically increasing integer within the owning agent, starting at 1 for the initial version. + /// Maps to the agent_versions.version_number column. /// public required int VersionNumber { get; init; } /// - /// Gets or sets the system prompt. + /// JSON-serialized system prompt (name and template text) active in this version. + /// Maps to the agent_versions.system_prompt column. /// public required SystemPromptData SystemPrompt { get; init; } /// - /// Gets or sets the tools. + /// JSON-serialized list of tool specifications available to the agent in this version. + /// Maps to the agent_versions.tools column. /// public required IReadOnlyList Tools { get; init; } diff --git a/Proxytrace.Storage/Internal/Entities/AgentVersion/AgentVersionRepository.cs b/Proxytrace.Storage/Internal/Entities/AgentVersion/AgentVersionRepository.cs index 89ddbaddc..90f172494 100644 --- a/Proxytrace.Storage/Internal/Entities/AgentVersion/AgentVersionRepository.cs +++ b/Proxytrace.Storage/Internal/Entities/AgentVersion/AgentVersionRepository.cs @@ -17,7 +17,8 @@ internal class AgentVersionRepository : AbstractRepository - /// Initializes a new instance of the class. + /// Initializes the repository with the fingerprinter used for strict and loose fingerprint + /// lookups, plus the version cache for fast repeated resolution. /// public AgentVersionRepository( IMapper mapper, @@ -32,7 +33,9 @@ public AgentVersionRepository( } /// - /// Finds the by strict fingerprint asynchronously. + /// Returns the agent version in the given project whose strict fingerprint (SHA-256 of system + /// prompt plus all tool specifications with descriptions) matches the given prompt and tools, or + /// null if no match exists. Used by GetOrCreateAsync to detect an exact duplicate before inserting. /// public async Task FindByStrictFingerprintAsync( IProject project,