Implement Phase 6R-AD provider billing settlement shell
This commit is contained in:
parent
fa0dab8942
commit
f488d2571f
14 changed files with 1679 additions and 32 deletions
|
|
@ -1086,6 +1086,68 @@ namespace HyperTwistContractLibraryInternal
|
|||
return Profile;
|
||||
}
|
||||
|
||||
FHyperTwistSpeechProviderBillingSettlementShellProfile
|
||||
MakeDefaultSpeechProviderBillingSettlementShellProfile(const FString& ProfileId)
|
||||
{
|
||||
FHyperTwistSpeechProviderBillingSettlementShellProfile Profile;
|
||||
Profile.BillingSettlementShellProfileId = ProfileId;
|
||||
Profile.ShellKind = TEXT("operator-provider-billing-settlement");
|
||||
Profile.SettlementSurfaceMode = TEXT("posted-charge-settlement-assessment");
|
||||
Profile.InvoiceReconciliationMode = TEXT("estimate-versus-provider-invoice");
|
||||
Profile.RefreshActionId = TEXT("refresh-provider-billing-settlement");
|
||||
Profile.InvoiceReconciliationActionId = TEXT("inspect-provider-invoice-reconciliation");
|
||||
Profile.SettlementReadinessActionId = TEXT("review-provider-settlement-readiness");
|
||||
Profile.RouteInspectActionId = TEXT("inspect-provider-route");
|
||||
Profile.BudgetInspectActionId = TEXT("review-provider-budget");
|
||||
Profile.RetainedSettlementEntryLimit = 12;
|
||||
Profile.bSupportsSettlementTimeline = true;
|
||||
Profile.bSupportsInvoiceReconciliation = true;
|
||||
Profile.bSupportsSettlementReadinessReview = true;
|
||||
Profile.bSupportsEstimateOnlyFallback = true;
|
||||
Profile.bPreservesFirstPartySettlementOwnership = true;
|
||||
|
||||
auto AddPanel = [&Profile](
|
||||
const TCHAR* PanelId,
|
||||
const TCHAR* PanelKind,
|
||||
const TCHAR* AnchorId,
|
||||
const bool bVisibleByDefault
|
||||
)
|
||||
{
|
||||
FHyperTwistSpeechShellPanelLayout Panel;
|
||||
Panel.PanelId = PanelId;
|
||||
Panel.PanelKind = PanelKind;
|
||||
Panel.AnchorId = AnchorId;
|
||||
Panel.bVisibleByDefault = bVisibleByDefault;
|
||||
Profile.Panels.Add(Panel);
|
||||
};
|
||||
|
||||
auto AddAction = [&Profile](
|
||||
const TCHAR* ActionId,
|
||||
const TCHAR* InputBinding,
|
||||
const TCHAR* SurfaceId
|
||||
)
|
||||
{
|
||||
FHyperTwistSpeechShellActionBinding ActionBinding;
|
||||
ActionBinding.ActionId = ActionId;
|
||||
ActionBinding.InputBinding = InputBinding;
|
||||
ActionBinding.SurfaceId = SurfaceId;
|
||||
Profile.ActionBindings.Add(ActionBinding);
|
||||
};
|
||||
|
||||
AddPanel(TEXT("settlement-timeline-card"), TEXT("settlement-timeline-card"), TEXT("top-left"), true);
|
||||
AddPanel(TEXT("invoice-reconciliation-card"), TEXT("invoice-reconciliation-card"), TEXT("top-right"), true);
|
||||
AddPanel(TEXT("settlement-summary-card"), TEXT("settlement-summary-card"), TEXT("left-stack-below-settlement"), true);
|
||||
AddPanel(TEXT("settlement-readiness-card"), TEXT("settlement-readiness-card"), TEXT("right-stack-below-reconciliation"), true);
|
||||
|
||||
AddAction(TEXT("refresh-provider-billing-settlement"), TEXT("F8"), TEXT("settlement-timeline-card"));
|
||||
AddAction(TEXT("inspect-provider-invoice-reconciliation"), TEXT("R"), TEXT("invoice-reconciliation-card"));
|
||||
AddAction(TEXT("review-provider-settlement-readiness"), TEXT("S"), TEXT("settlement-readiness-card"));
|
||||
AddAction(TEXT("inspect-provider-route"), TEXT("P"), TEXT("settlement-summary-card"));
|
||||
AddAction(TEXT("review-provider-budget"), TEXT("B"), TEXT("settlement-readiness-card"));
|
||||
AddAction(TEXT("close-speech-session"), TEXT("Escape"), TEXT("session-root"));
|
||||
return Profile;
|
||||
}
|
||||
|
||||
FHyperTwistSpeechModelPayloadDescriptor MakeSpeechModelPayloadDescriptor(
|
||||
const TCHAR* PayloadId,
|
||||
const TCHAR* PayloadKind,
|
||||
|
|
@ -1173,7 +1235,10 @@ namespace HyperTwistContractLibraryInternal
|
|||
TEXT("providerUsageCostSummaryCopy"),
|
||||
TEXT("providerReceiptReviewShell"),
|
||||
TEXT("providerPostedChargeInspection"),
|
||||
TEXT("providerChargeVarianceReview")
|
||||
TEXT("providerChargeVarianceReview"),
|
||||
TEXT("providerBillingSettlementShell"),
|
||||
TEXT("providerInvoiceReconciliation"),
|
||||
TEXT("providerSettlementReadinessReview")
|
||||
};
|
||||
}
|
||||
|
||||
|
|
@ -2411,6 +2476,12 @@ FHyperTwistSpeechSessionConfig UHyperTwistContractLibrary::MakeSampleSpeechSessi
|
|||
HyperTwistContractLibraryInternal::MakeDefaultSpeechProviderReceiptReviewShellProfile(
|
||||
Config.ProviderReceiptReviewShellProfileId
|
||||
);
|
||||
Config.ProviderBillingSettlementShellProfileId =
|
||||
TEXT("speech-provider-billing-settlement-shell-v1");
|
||||
Config.ProviderBillingSettlementShellProfileDefinition =
|
||||
HyperTwistContractLibraryInternal::MakeDefaultSpeechProviderBillingSettlementShellProfile(
|
||||
Config.ProviderBillingSettlementShellProfileId
|
||||
);
|
||||
Config.PayloadCustodyProfileId = TEXT("downloadable-model-payload-custody-v1");
|
||||
Config.PayloadCustodyProfileDefinition =
|
||||
HyperTwistContractLibraryInternal::MakeDefaultSpeechPayloadCustodyProfile(
|
||||
|
|
@ -2527,7 +2598,10 @@ FHyperTwistSpeechServiceHealth UHyperTwistContractLibrary::MakeMockSpeechService
|
|||
TEXT("providerUsageCostSummaryCopy"),
|
||||
TEXT("providerReceiptReviewShell"),
|
||||
TEXT("providerPostedChargeInspection"),
|
||||
TEXT("providerChargeVarianceReview")
|
||||
TEXT("providerChargeVarianceReview"),
|
||||
TEXT("providerBillingSettlementShell"),
|
||||
TEXT("providerInvoiceReconciliation"),
|
||||
TEXT("providerSettlementReadinessReview")
|
||||
};
|
||||
Health.SupportedOrchestrationProfiles = {
|
||||
TEXT("python-batch-transcribe-v1")
|
||||
|
|
|
|||
|
|
@ -350,7 +350,10 @@ FHyperTwistSpeechServiceHealth UHyperTwistHttpSpeechClient::GetSpeechServiceHeal
|
|||
TEXT("providerUsageCostSummaryCopy"),
|
||||
TEXT("providerReceiptReviewShell"),
|
||||
TEXT("providerPostedChargeInspection"),
|
||||
TEXT("providerChargeVarianceReview")
|
||||
TEXT("providerChargeVarianceReview"),
|
||||
TEXT("providerBillingSettlementShell"),
|
||||
TEXT("providerInvoiceReconciliation"),
|
||||
TEXT("providerSettlementReadinessReview")
|
||||
};
|
||||
Health.SupportedOrchestrationProfiles = {
|
||||
TEXT("python-batch-transcribe-v1")
|
||||
|
|
@ -433,7 +436,10 @@ FHyperTwistSpeechServiceHealth UHyperTwistHttpSpeechClient::GetSpeechServiceHeal
|
|||
TEXT("providerUsageCostSummaryCopy"),
|
||||
TEXT("providerReceiptReviewShell"),
|
||||
TEXT("providerPostedChargeInspection"),
|
||||
TEXT("providerChargeVarianceReview")
|
||||
TEXT("providerChargeVarianceReview"),
|
||||
TEXT("providerBillingSettlementShell"),
|
||||
TEXT("providerInvoiceReconciliation"),
|
||||
TEXT("providerSettlementReadinessReview")
|
||||
};
|
||||
Health.SupportedOrchestrationProfiles = {
|
||||
TEXT("python-batch-transcribe-v1")
|
||||
|
|
@ -501,6 +507,9 @@ FHyperTwistSpeechServiceHealth UHyperTwistHttpSpeechClient::GetSpeechServiceHeal
|
|||
Health.Capabilities.AddUnique(TEXT("providerReceiptReviewShell"));
|
||||
Health.Capabilities.AddUnique(TEXT("providerPostedChargeInspection"));
|
||||
Health.Capabilities.AddUnique(TEXT("providerChargeVarianceReview"));
|
||||
Health.Capabilities.AddUnique(TEXT("providerBillingSettlementShell"));
|
||||
Health.Capabilities.AddUnique(TEXT("providerInvoiceReconciliation"));
|
||||
Health.Capabilities.AddUnique(TEXT("providerSettlementReadinessReview"));
|
||||
Health.SupportedOrchestrationProfiles.AddUnique(TEXT("python-batch-transcribe-v1"));
|
||||
if (Health.ProviderProfileId.IsEmpty())
|
||||
{
|
||||
|
|
|
|||
|
|
@ -155,6 +155,9 @@ FHyperTwistSpeechServiceHealth UHyperTwistMockSpeechClient::GetSpeechServiceHeal
|
|||
Health.Capabilities.AddUnique(TEXT("providerReceiptReviewShell"));
|
||||
Health.Capabilities.AddUnique(TEXT("providerPostedChargeInspection"));
|
||||
Health.Capabilities.AddUnique(TEXT("providerChargeVarianceReview"));
|
||||
Health.Capabilities.AddUnique(TEXT("providerBillingSettlementShell"));
|
||||
Health.Capabilities.AddUnique(TEXT("providerInvoiceReconciliation"));
|
||||
Health.Capabilities.AddUnique(TEXT("providerSettlementReadinessReview"));
|
||||
Health.SupportedOrchestrationProfiles.AddUnique(TEXT("python-batch-transcribe-v1"));
|
||||
return Health;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2931,6 +2931,11 @@ namespace HyperTwistTrainingSubsystemInternal
|
|||
const FHyperTwistTrainingCompanionSpeechSessionState* PriorState
|
||||
);
|
||||
|
||||
FHyperTwistSpeechProviderBillingSettlementShellState BuildSpeechProviderBillingSettlementShellState(
|
||||
const FHyperTwistSpeechSessionConfig& SessionConfig,
|
||||
const FHyperTwistTrainingCompanionSpeechSessionState* PriorState
|
||||
);
|
||||
|
||||
FHyperTwistSpeechMicrophoneShellProfile BuildDefaultCoachCommandMicrophoneShellProfile(const FString& ProfileId)
|
||||
{
|
||||
FHyperTwistSpeechMicrophoneShellProfile Profile;
|
||||
|
|
@ -3176,6 +3181,68 @@ namespace HyperTwistTrainingSubsystemInternal
|
|||
return Profile;
|
||||
}
|
||||
|
||||
FHyperTwistSpeechProviderBillingSettlementShellProfile
|
||||
BuildDefaultSpeechProviderBillingSettlementShellProfile(const FString& ProfileId)
|
||||
{
|
||||
FHyperTwistSpeechProviderBillingSettlementShellProfile Profile;
|
||||
Profile.BillingSettlementShellProfileId = ProfileId;
|
||||
Profile.ShellKind = TEXT("operator-provider-billing-settlement");
|
||||
Profile.SettlementSurfaceMode = TEXT("posted-charge-settlement-assessment");
|
||||
Profile.InvoiceReconciliationMode = TEXT("estimate-versus-provider-invoice");
|
||||
Profile.RefreshActionId = TEXT("refresh-provider-billing-settlement");
|
||||
Profile.InvoiceReconciliationActionId = TEXT("inspect-provider-invoice-reconciliation");
|
||||
Profile.SettlementReadinessActionId = TEXT("review-provider-settlement-readiness");
|
||||
Profile.RouteInspectActionId = TEXT("inspect-provider-route");
|
||||
Profile.BudgetInspectActionId = TEXT("review-provider-budget");
|
||||
Profile.RetainedSettlementEntryLimit = 12;
|
||||
Profile.bSupportsSettlementTimeline = true;
|
||||
Profile.bSupportsInvoiceReconciliation = true;
|
||||
Profile.bSupportsSettlementReadinessReview = true;
|
||||
Profile.bSupportsEstimateOnlyFallback = true;
|
||||
Profile.bPreservesFirstPartySettlementOwnership = true;
|
||||
|
||||
auto AddPanel = [&Profile](
|
||||
const TCHAR* PanelId,
|
||||
const TCHAR* PanelKind,
|
||||
const TCHAR* AnchorId,
|
||||
const bool bVisibleByDefault
|
||||
)
|
||||
{
|
||||
FHyperTwistSpeechShellPanelLayout Panel;
|
||||
Panel.PanelId = PanelId;
|
||||
Panel.PanelKind = PanelKind;
|
||||
Panel.AnchorId = AnchorId;
|
||||
Panel.bVisibleByDefault = bVisibleByDefault;
|
||||
Profile.Panels.Add(Panel);
|
||||
};
|
||||
|
||||
auto AddAction = [&Profile](
|
||||
const TCHAR* ActionId,
|
||||
const TCHAR* InputBinding,
|
||||
const TCHAR* SurfaceId
|
||||
)
|
||||
{
|
||||
FHyperTwistSpeechShellActionBinding ActionBinding;
|
||||
ActionBinding.ActionId = ActionId;
|
||||
ActionBinding.InputBinding = InputBinding;
|
||||
ActionBinding.SurfaceId = SurfaceId;
|
||||
Profile.ActionBindings.Add(ActionBinding);
|
||||
};
|
||||
|
||||
AddPanel(TEXT("settlement-timeline-card"), TEXT("settlement-timeline-card"), TEXT("top-left"), true);
|
||||
AddPanel(TEXT("invoice-reconciliation-card"), TEXT("invoice-reconciliation-card"), TEXT("top-right"), true);
|
||||
AddPanel(TEXT("settlement-summary-card"), TEXT("settlement-summary-card"), TEXT("left-stack-below-settlement"), true);
|
||||
AddPanel(TEXT("settlement-readiness-card"), TEXT("settlement-readiness-card"), TEXT("right-stack-below-reconciliation"), true);
|
||||
|
||||
AddAction(TEXT("refresh-provider-billing-settlement"), TEXT("F8"), TEXT("settlement-timeline-card"));
|
||||
AddAction(TEXT("inspect-provider-invoice-reconciliation"), TEXT("R"), TEXT("invoice-reconciliation-card"));
|
||||
AddAction(TEXT("review-provider-settlement-readiness"), TEXT("S"), TEXT("settlement-readiness-card"));
|
||||
AddAction(TEXT("inspect-provider-route"), TEXT("P"), TEXT("settlement-summary-card"));
|
||||
AddAction(TEXT("review-provider-budget"), TEXT("B"), TEXT("settlement-readiness-card"));
|
||||
AddAction(TEXT("close-speech-session"), TEXT("Escape"), TEXT("session-root"));
|
||||
return Profile;
|
||||
}
|
||||
|
||||
FHyperTwistSpeechModelPayloadDescriptor MakeSpeechModelPayloadDescriptor(
|
||||
const TCHAR* PayloadId,
|
||||
const TCHAR* PayloadKind,
|
||||
|
|
@ -3263,7 +3330,10 @@ namespace HyperTwistTrainingSubsystemInternal
|
|||
TEXT("providerUsageCostSummaryCopy"),
|
||||
TEXT("providerReceiptReviewShell"),
|
||||
TEXT("providerPostedChargeInspection"),
|
||||
TEXT("providerChargeVarianceReview")
|
||||
TEXT("providerChargeVarianceReview"),
|
||||
TEXT("providerBillingSettlementShell"),
|
||||
TEXT("providerInvoiceReconciliation"),
|
||||
TEXT("providerSettlementReadinessReview")
|
||||
};
|
||||
}
|
||||
|
||||
|
|
@ -3958,6 +4028,26 @@ namespace HyperTwistTrainingSubsystemInternal
|
|||
}
|
||||
SessionConfig.ProviderReceiptReviewShellProfileId =
|
||||
SessionConfig.ProviderReceiptReviewShellProfileDefinition.ReceiptReviewShellProfileId;
|
||||
if (SessionConfig.ProviderBillingSettlementShellProfileId.IsEmpty()
|
||||
&& SessionConfig.ProviderBillingSettlementShellProfileDefinition.IsStructurallyValid())
|
||||
{
|
||||
SessionConfig.ProviderBillingSettlementShellProfileId =
|
||||
SessionConfig.ProviderBillingSettlementShellProfileDefinition.BillingSettlementShellProfileId;
|
||||
}
|
||||
if (SessionConfig.ProviderBillingSettlementShellProfileId.IsEmpty())
|
||||
{
|
||||
SessionConfig.ProviderBillingSettlementShellProfileId =
|
||||
TEXT("speech-provider-billing-settlement-shell-v1");
|
||||
}
|
||||
if (!SessionConfig.ProviderBillingSettlementShellProfileDefinition.IsStructurallyValid())
|
||||
{
|
||||
SessionConfig.ProviderBillingSettlementShellProfileDefinition =
|
||||
BuildDefaultSpeechProviderBillingSettlementShellProfile(
|
||||
SessionConfig.ProviderBillingSettlementShellProfileId
|
||||
);
|
||||
}
|
||||
SessionConfig.ProviderBillingSettlementShellProfileId =
|
||||
SessionConfig.ProviderBillingSettlementShellProfileDefinition.BillingSettlementShellProfileId;
|
||||
}
|
||||
|
||||
void ApplySpeechProviderProfileHealthDefaults(
|
||||
|
|
@ -4296,6 +4386,9 @@ namespace HyperTwistTrainingSubsystemInternal
|
|||
Health.Capabilities.AddUnique(TEXT("providerReceiptReviewShell"));
|
||||
Health.Capabilities.AddUnique(TEXT("providerPostedChargeInspection"));
|
||||
Health.Capabilities.AddUnique(TEXT("providerChargeVarianceReview"));
|
||||
Health.Capabilities.AddUnique(TEXT("providerBillingSettlementShell"));
|
||||
Health.Capabilities.AddUnique(TEXT("providerInvoiceReconciliation"));
|
||||
Health.Capabilities.AddUnique(TEXT("providerSettlementReadinessReview"));
|
||||
}
|
||||
|
||||
bool IsSpeechPermissionError(const FString& ErrorText)
|
||||
|
|
@ -5469,6 +5562,361 @@ namespace HyperTwistTrainingSubsystemInternal
|
|||
}
|
||||
}
|
||||
|
||||
FString BuildSpeechProviderBillingSettlementSourceKind(
|
||||
const FHyperTwistSpeechProviderReceiptReviewEntry& ReceiptEntry
|
||||
)
|
||||
{
|
||||
if (ReceiptEntry.SourceKind.Contains(TEXT("fallback"), ESearchCase::IgnoreCase))
|
||||
{
|
||||
return TEXT("receipt-review-fallback-health");
|
||||
}
|
||||
|
||||
if (ReceiptEntry.bHasProviderPostedCharge)
|
||||
{
|
||||
return TEXT("receipt-review-derived-posted-charge");
|
||||
}
|
||||
|
||||
if (ReceiptEntry.bEstimateOnly)
|
||||
{
|
||||
return TEXT("receipt-review-derived-estimate-only");
|
||||
}
|
||||
|
||||
return TEXT("receipt-review-derived-mixed");
|
||||
}
|
||||
|
||||
FHyperTwistSpeechProviderBillingSettlementEntry BuildSpeechProviderBillingSettlementEntry(
|
||||
const FHyperTwistSpeechProviderReceiptReviewEntry& ReceiptEntry,
|
||||
const FString& SettlementWindowId
|
||||
)
|
||||
{
|
||||
FHyperTwistSpeechProviderBillingSettlementEntry Entry;
|
||||
Entry.SettlementEntryId = FString::Printf(
|
||||
TEXT("speech-provider-settlement-entry/%s"),
|
||||
*ReceiptEntry.ReceiptEntryId
|
||||
);
|
||||
Entry.SourceKind = BuildSpeechProviderBillingSettlementSourceKind(ReceiptEntry);
|
||||
Entry.ReceiptEntryId = ReceiptEntry.ReceiptEntryId;
|
||||
Entry.HistoryEntryId = ReceiptEntry.HistoryEntryId;
|
||||
Entry.ProviderProfileId = ReceiptEntry.ProviderProfileId;
|
||||
Entry.ServiceLaneId = ReceiptEntry.ServiceLaneId;
|
||||
Entry.PricingSourceId = ReceiptEntry.PricingSourceId;
|
||||
Entry.ChargeWindowId = ReceiptEntry.ChargeWindowId;
|
||||
Entry.SettlementWindowId = SettlementWindowId.IsEmpty()
|
||||
? TEXT("current-session")
|
||||
: SettlementWindowId;
|
||||
Entry.CapturedAtUtc = ReceiptEntry.CapturedAtUtc;
|
||||
Entry.EstimatedCostUsd = ReceiptEntry.EstimatedCostUsd;
|
||||
Entry.ReportedCostUsd = ReceiptEntry.ReportedCostUsd;
|
||||
Entry.SettlementDeltaUsd = ReceiptEntry.AbsoluteVarianceUsd;
|
||||
Entry.bHasProviderPostedCharge = ReceiptEntry.bHasProviderPostedCharge;
|
||||
Entry.bRequiresManualReview =
|
||||
ReceiptEntry.bHasProviderPostedCharge && !ReceiptEntry.bVarianceWithinTolerance;
|
||||
Entry.bInvoiceReconciliationReady =
|
||||
ReceiptEntry.bHasProviderPostedCharge && !Entry.bRequiresManualReview;
|
||||
Entry.bSettlementBlocked = !Entry.bInvoiceReconciliationReady;
|
||||
|
||||
if (!ReceiptEntry.bHasProviderPostedCharge)
|
||||
{
|
||||
Entry.SettlementStateId = TEXT("awaiting-provider-posted-charge");
|
||||
Entry.InvoiceReconciliationStateId = TEXT("invoice-pending");
|
||||
}
|
||||
else if (Entry.bRequiresManualReview)
|
||||
{
|
||||
Entry.SettlementStateId = TEXT("variance-review-required");
|
||||
Entry.InvoiceReconciliationStateId = TEXT("invoice-variance-review-required");
|
||||
}
|
||||
else
|
||||
{
|
||||
Entry.SettlementStateId = TEXT("ready-for-settlement-review");
|
||||
Entry.InvoiceReconciliationStateId = TEXT("invoice-ready-for-reconciliation");
|
||||
}
|
||||
|
||||
return Entry;
|
||||
}
|
||||
|
||||
void AddSpeechProviderBillingSettlementIssue(
|
||||
FHyperTwistSpeechProviderBillingSettlementShellState& ShellState,
|
||||
const FHyperTwistSpeechProviderBillingSettlementIssue& Issue
|
||||
)
|
||||
{
|
||||
if (!Issue.IsStructurallyValid())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
ShellState.Issues.Add(Issue);
|
||||
if (!ShellState.ActiveIssue.IsStructurallyValid())
|
||||
{
|
||||
ShellState.ActiveIssue = Issue;
|
||||
}
|
||||
}
|
||||
|
||||
void ApplySpeechProviderBillingSettlementPosture(
|
||||
const FHyperTwistSpeechProviderBillingSettlementShellProfile& Profile,
|
||||
const FHyperTwistTrainingCompanionSpeechSessionState& SessionState,
|
||||
FHyperTwistSpeechProviderBillingSettlementShellState& ShellState
|
||||
)
|
||||
{
|
||||
ShellState.ActiveProviderProfileId =
|
||||
!SessionState.ProviderReceiptReviewShellState.ActiveProviderProfileId.IsEmpty()
|
||||
? SessionState.ProviderReceiptReviewShellState.ActiveProviderProfileId
|
||||
: (!SessionState.SessionConfig.ProviderProfileDefinition.ProviderProfileId.IsEmpty()
|
||||
? SessionState.SessionConfig.ProviderProfileDefinition.ProviderProfileId
|
||||
: TEXT("speech-provider/local-http-sidecar-profile-v1"));
|
||||
ShellState.ActiveProviderDisplayLabel =
|
||||
!SessionState.ProviderReceiptReviewShellState.ActiveProviderDisplayLabel.IsEmpty()
|
||||
? SessionState.ProviderReceiptReviewShellState.ActiveProviderDisplayLabel
|
||||
: (!SessionState.SessionConfig.ProviderProfileDefinition.DisplayLabel.IsEmpty()
|
||||
? SessionState.SessionConfig.ProviderProfileDefinition.DisplayLabel
|
||||
: TEXT("Local HTTP Speech Sidecar"));
|
||||
ShellState.ActiveServiceLaneId =
|
||||
!SessionState.ProviderReceiptReviewShellState.ActiveServiceLaneId.IsEmpty()
|
||||
? SessionState.ProviderReceiptReviewShellState.ActiveServiceLaneId
|
||||
: (!SessionState.SessionConfig.OrchestrationProfile.ServiceLaneId.IsEmpty()
|
||||
? SessionState.SessionConfig.OrchestrationProfile.ServiceLaneId
|
||||
: TEXT("speech/python-service"));
|
||||
ShellState.SelectedSettlementWindowId =
|
||||
!SessionState.ProviderReceiptReviewShellState.SelectedChargeWindowId.IsEmpty()
|
||||
? SessionState.ProviderReceiptReviewShellState.SelectedChargeWindowId
|
||||
: TEXT("current-session");
|
||||
ShellState.SettlementSurfaceModeId = Profile.SettlementSurfaceMode;
|
||||
ShellState.InvoiceReconciliationModeId = Profile.InvoiceReconciliationMode;
|
||||
ShellState.bSettlementTimelineVisible = Profile.bSupportsSettlementTimeline;
|
||||
ShellState.bInvoiceReconciliationVisible = Profile.bSupportsInvoiceReconciliation;
|
||||
ShellState.bSettlementReadinessVisible = Profile.bSupportsSettlementReadinessReview;
|
||||
ShellState.SettlementEntries.Reset();
|
||||
ShellState.Issues.Reset();
|
||||
ShellState.ActiveIssue = FHyperTwistSpeechProviderBillingSettlementIssue();
|
||||
ShellState.SettlementEntryCount = 0;
|
||||
ShellState.ProviderPostedChargeCount = 0;
|
||||
ShellState.ReconciliationReadyEntryCount = 0;
|
||||
ShellState.EstimateOnlyEntryCount = 0;
|
||||
ShellState.ManualReviewEntryCount = 0;
|
||||
ShellState.BlockedSettlementEntryCount = 0;
|
||||
ShellState.TotalEstimatedCostUsd = 0.0f;
|
||||
ShellState.TotalReportedCostUsd = 0.0f;
|
||||
ShellState.TotalSettlementDeltaUsd = 0.0f;
|
||||
ShellState.HighestSettlementDeltaUsd = 0.0f;
|
||||
ShellState.LatestSettlementEntryId.Reset();
|
||||
ShellState.bSettlementSummaryReady = false;
|
||||
ShellState.bInvoiceReconciliationReady = false;
|
||||
ShellState.bSettlementReadinessReviewReady = false;
|
||||
|
||||
const FHyperTwistSpeechProviderReceiptReviewShellState& ReceiptState =
|
||||
SessionState.ProviderReceiptReviewShellState;
|
||||
if (!ReceiptState.IsStructurallyValid() || ReceiptState.ReceiptEntries.Num() <= 0)
|
||||
{
|
||||
FHyperTwistSpeechProviderBillingSettlementIssue Issue;
|
||||
Issue.IssueId = TEXT("provider-billing-settlement-receipt-review-missing");
|
||||
Issue.IssueKind = TEXT("no-receipt-review");
|
||||
Issue.StatusLine = TEXT("Provider billing settlement is waiting for receipt review.");
|
||||
Issue.DetailLine = TEXT("Refresh the retained provider receipt-review shell before settlement posture begins.");
|
||||
Issue.RecommendedActionId = Profile.RefreshActionId;
|
||||
Issue.bRequiresOperatorAttention = false;
|
||||
AddSpeechProviderBillingSettlementIssue(ShellState, Issue);
|
||||
ShellState.StatusLine = Issue.StatusLine;
|
||||
ShellState.DetailLine = Issue.DetailLine;
|
||||
ShellState.ActiveIssueCount = ShellState.Issues.Num();
|
||||
return;
|
||||
}
|
||||
|
||||
const int32 SettlementLimit = FMath::Max(1, Profile.RetainedSettlementEntryLimit);
|
||||
const int32 StartIndex = FMath::Max(0, ReceiptState.ReceiptEntries.Num() - SettlementLimit);
|
||||
for (int32 EntryIndex = StartIndex; EntryIndex < ReceiptState.ReceiptEntries.Num(); ++EntryIndex)
|
||||
{
|
||||
const FHyperTwistSpeechProviderReceiptReviewEntry& ReceiptEntry =
|
||||
ReceiptState.ReceiptEntries[EntryIndex];
|
||||
if (!ReceiptEntry.IsStructurallyValid())
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
const FHyperTwistSpeechProviderBillingSettlementEntry Entry =
|
||||
BuildSpeechProviderBillingSettlementEntry(
|
||||
ReceiptEntry,
|
||||
ShellState.SelectedSettlementWindowId
|
||||
);
|
||||
ShellState.SettlementEntries.Add(Entry);
|
||||
ShellState.LatestSettlementEntryId = Entry.SettlementEntryId;
|
||||
ShellState.LatestSourceKind = Entry.SourceKind;
|
||||
ShellState.TotalEstimatedCostUsd += Entry.EstimatedCostUsd;
|
||||
ShellState.TotalReportedCostUsd += Entry.ReportedCostUsd;
|
||||
ShellState.TotalSettlementDeltaUsd += Entry.SettlementDeltaUsd;
|
||||
ShellState.HighestSettlementDeltaUsd =
|
||||
FMath::Max(ShellState.HighestSettlementDeltaUsd, Entry.SettlementDeltaUsd);
|
||||
if (Entry.bHasProviderPostedCharge)
|
||||
{
|
||||
++ShellState.ProviderPostedChargeCount;
|
||||
}
|
||||
if (Entry.bInvoiceReconciliationReady)
|
||||
{
|
||||
++ShellState.ReconciliationReadyEntryCount;
|
||||
}
|
||||
if (ReceiptEntry.bEstimateOnly)
|
||||
{
|
||||
++ShellState.EstimateOnlyEntryCount;
|
||||
}
|
||||
if (Entry.bRequiresManualReview)
|
||||
{
|
||||
++ShellState.ManualReviewEntryCount;
|
||||
}
|
||||
if (Entry.bSettlementBlocked)
|
||||
{
|
||||
++ShellState.BlockedSettlementEntryCount;
|
||||
}
|
||||
}
|
||||
|
||||
ShellState.SettlementEntryCount = ShellState.SettlementEntries.Num();
|
||||
ShellState.bSettlementSummaryReady = ShellState.SettlementEntryCount > 0;
|
||||
ShellState.bInvoiceReconciliationReady =
|
||||
Profile.bSupportsInvoiceReconciliation
|
||||
&& ShellState.ReconciliationReadyEntryCount > 0;
|
||||
ShellState.bSettlementReadinessReviewReady =
|
||||
Profile.bSupportsSettlementReadinessReview
|
||||
&& ShellState.SettlementEntryCount > 0;
|
||||
|
||||
if (ReceiptState.ActiveIssue.IsStructurallyValid()
|
||||
&& (ReceiptState.ActiveIssue.IssueKind.Equals(TEXT("route-degraded"), ESearchCase::IgnoreCase)
|
||||
|| ReceiptState.ActiveIssue.IssueKind.Equals(TEXT("hard-blocked"), ESearchCase::IgnoreCase)
|
||||
|| ReceiptState.ActiveIssue.IssueKind.Equals(TEXT("rate-limited"), ESearchCase::IgnoreCase)
|
||||
|| ReceiptState.ActiveIssue.IssueKind.Equals(TEXT("no-history"), ESearchCase::IgnoreCase)))
|
||||
{
|
||||
FHyperTwistSpeechProviderBillingSettlementIssue Issue;
|
||||
Issue.IssueId = FString::Printf(
|
||||
TEXT("provider-billing-settlement-%s"),
|
||||
*ReceiptState.ActiveIssue.IssueKind
|
||||
);
|
||||
Issue.IssueKind = ReceiptState.ActiveIssue.IssueKind;
|
||||
Issue.StatusLine = ReceiptState.ActiveIssue.StatusLine;
|
||||
Issue.DetailLine = ReceiptState.ActiveIssue.DetailLine;
|
||||
Issue.RecommendedActionId = ReceiptState.ActiveIssue.RecommendedActionId;
|
||||
Issue.bRequiresOperatorAttention =
|
||||
ReceiptState.ActiveIssue.bRequiresOperatorAttention;
|
||||
AddSpeechProviderBillingSettlementIssue(ShellState, Issue);
|
||||
}
|
||||
else if (ShellState.ManualReviewEntryCount > 0)
|
||||
{
|
||||
FHyperTwistSpeechProviderBillingSettlementIssue Issue;
|
||||
Issue.IssueId = TEXT("provider-billing-settlement-variance-review-required");
|
||||
Issue.IssueKind = TEXT("settlement-variance-review-required");
|
||||
Issue.StatusLine = TEXT("Settlement variance requires review before reconciliation.");
|
||||
Issue.DetailLine = TEXT("At least one posted provider charge still diverges from the first-party estimate beyond the bounded receipt-review tolerance.");
|
||||
Issue.RecommendedActionId = Profile.SettlementReadinessActionId;
|
||||
Issue.bRequiresOperatorAttention = true;
|
||||
AddSpeechProviderBillingSettlementIssue(ShellState, Issue);
|
||||
}
|
||||
else if (ShellState.ProviderPostedChargeCount <= 0)
|
||||
{
|
||||
FHyperTwistSpeechProviderBillingSettlementIssue Issue;
|
||||
Issue.IssueId = TEXT("provider-billing-settlement-charge-pending");
|
||||
Issue.IssueKind = TEXT("provider-charge-pending");
|
||||
Issue.StatusLine = TEXT("Provider billing settlement is waiting for posted charges.");
|
||||
Issue.DetailLine = TEXT("Settlement posture is bounded and ready, but the retained receipt review still shows estimate-only provider cost posture.");
|
||||
Issue.RecommendedActionId = Profile.InvoiceReconciliationActionId;
|
||||
Issue.bRequiresOperatorAttention = false;
|
||||
AddSpeechProviderBillingSettlementIssue(ShellState, Issue);
|
||||
}
|
||||
|
||||
if (ShellState.ActiveIssue.IsStructurallyValid())
|
||||
{
|
||||
ShellState.StatusLine = ShellState.ActiveIssue.StatusLine;
|
||||
ShellState.DetailLine = ShellState.ActiveIssue.DetailLine;
|
||||
}
|
||||
else if (ShellState.ReconciliationReadyEntryCount > 0)
|
||||
{
|
||||
ShellState.StatusLine =
|
||||
TEXT("Provider billing settlement is ready for bounded reconciliation.");
|
||||
ShellState.DetailLine = FString::Printf(
|
||||
TEXT("%d retained posted charges for %s are within the current first-party tolerance and ready for invoice reconciliation review."),
|
||||
ShellState.ReconciliationReadyEntryCount,
|
||||
*ShellState.ActiveProviderDisplayLabel
|
||||
);
|
||||
}
|
||||
else
|
||||
{
|
||||
ShellState.StatusLine =
|
||||
TEXT("Provider billing settlement remains bounded above receipt review.");
|
||||
ShellState.DetailLine = FString::Printf(
|
||||
TEXT("%d retained settlement entries are available for %s, but a reconciled provider invoice posture is not ready yet."),
|
||||
ShellState.SettlementEntryCount,
|
||||
*ShellState.ActiveProviderDisplayLabel
|
||||
);
|
||||
}
|
||||
|
||||
ShellState.SettlementSummaryText = FString::Printf(
|
||||
TEXT("providerProfileId=%s\nserviceLaneId=%s\nsettlementEntryCount=%d\nproviderPostedChargeCount=%d\n")
|
||||
TEXT("reconciliationReadyEntryCount=%d\nestimateOnlyEntryCount=%d\nmanualReviewEntryCount=%d\n")
|
||||
TEXT("blockedSettlementEntryCount=%d\ntotalEstimatedCostUsd=%.2f\ntotalReportedCostUsd=%.2f\n")
|
||||
TEXT("totalSettlementDeltaUsd=%.2f\nhighestSettlementDeltaUsd=%.2f\nlatestSettlementEntryId=%s\n")
|
||||
TEXT("latestSourceKind=%s\nlastError=%s"),
|
||||
*ShellState.ActiveProviderProfileId,
|
||||
*ShellState.ActiveServiceLaneId,
|
||||
ShellState.SettlementEntryCount,
|
||||
ShellState.ProviderPostedChargeCount,
|
||||
ShellState.ReconciliationReadyEntryCount,
|
||||
ShellState.EstimateOnlyEntryCount,
|
||||
ShellState.ManualReviewEntryCount,
|
||||
ShellState.BlockedSettlementEntryCount,
|
||||
ShellState.TotalEstimatedCostUsd,
|
||||
ShellState.TotalReportedCostUsd,
|
||||
ShellState.TotalSettlementDeltaUsd,
|
||||
ShellState.HighestSettlementDeltaUsd,
|
||||
*ShellState.LatestSettlementEntryId,
|
||||
*ShellState.LatestSourceKind,
|
||||
*SessionState.LastError
|
||||
);
|
||||
ShellState.ActiveIssueCount = ShellState.Issues.Num();
|
||||
}
|
||||
|
||||
void SynchronizeSpeechProviderBillingSettlementState(
|
||||
FHyperTwistTrainingCompanionSpeechSessionState& SessionState
|
||||
)
|
||||
{
|
||||
FHyperTwistSpeechSessionConfig EffectiveConfig = SessionState.SessionConfig;
|
||||
ApplySpeechUsageCostDefaults(EffectiveConfig);
|
||||
if (!SessionState.ProviderReceiptReviewShellState.IsStructurallyValid())
|
||||
{
|
||||
SessionState.ProviderReceiptReviewShellState =
|
||||
BuildSpeechProviderReceiptReviewShellState(EffectiveConfig, &SessionState);
|
||||
}
|
||||
|
||||
const FHyperTwistSpeechProviderBillingSettlementShellProfile Profile =
|
||||
EffectiveConfig.ProviderBillingSettlementShellProfileDefinition.IsStructurallyValid()
|
||||
? EffectiveConfig.ProviderBillingSettlementShellProfileDefinition
|
||||
: BuildDefaultSpeechProviderBillingSettlementShellProfile(
|
||||
!EffectiveConfig.ProviderBillingSettlementShellProfileId.IsEmpty()
|
||||
? EffectiveConfig.ProviderBillingSettlementShellProfileId
|
||||
: TEXT("speech-provider-billing-settlement-shell-v1")
|
||||
);
|
||||
|
||||
if (!SessionState.ProviderBillingSettlementShellState.IsStructurallyValid())
|
||||
{
|
||||
SessionState.ProviderBillingSettlementShellState =
|
||||
BuildSpeechProviderBillingSettlementShellState(EffectiveConfig, &SessionState);
|
||||
}
|
||||
|
||||
FHyperTwistSpeechProviderBillingSettlementShellState& ShellState =
|
||||
SessionState.ProviderBillingSettlementShellState;
|
||||
ShellState.BillingSettlementShellProfileId = Profile.BillingSettlementShellProfileId;
|
||||
ShellState.AvailableActionIds.Reset();
|
||||
for (const FHyperTwistSpeechShellActionBinding& ActionBinding : Profile.ActionBindings)
|
||||
{
|
||||
ShellState.AvailableActionIds.AddUnique(ActionBinding.ActionId);
|
||||
}
|
||||
|
||||
ApplySpeechProviderBillingSettlementPosture(Profile, SessionState, ShellState);
|
||||
|
||||
ShellState.AvailableActionIds.AddUnique(Profile.RefreshActionId);
|
||||
ShellState.AvailableActionIds.AddUnique(Profile.InvoiceReconciliationActionId);
|
||||
ShellState.AvailableActionIds.AddUnique(Profile.SettlementReadinessActionId);
|
||||
ShellState.AvailableActionIds.AddUnique(Profile.RouteInspectActionId);
|
||||
ShellState.AvailableActionIds.AddUnique(Profile.BudgetInspectActionId);
|
||||
if (ShellState.ActiveIssue.IsStructurallyValid())
|
||||
{
|
||||
ShellState.AvailableActionIds.AddUnique(ShellState.ActiveIssue.RecommendedActionId);
|
||||
}
|
||||
}
|
||||
|
||||
FHyperTwistSpeechMicrophoneShellState BuildCoachCommandMicrophoneShellState(
|
||||
const FHyperTwistSpeechSessionConfig& SessionConfig,
|
||||
const FHyperTwistTrainingCompanionSpeechSessionState* PriorState,
|
||||
|
|
@ -5722,6 +6170,57 @@ namespace HyperTwistTrainingSubsystemInternal
|
|||
return ShellState;
|
||||
}
|
||||
|
||||
FHyperTwistSpeechProviderBillingSettlementShellState BuildSpeechProviderBillingSettlementShellState(
|
||||
const FHyperTwistSpeechSessionConfig& SessionConfig,
|
||||
const FHyperTwistTrainingCompanionSpeechSessionState* PriorState
|
||||
)
|
||||
{
|
||||
const FHyperTwistSpeechProviderBillingSettlementShellProfile Profile =
|
||||
SessionConfig.ProviderBillingSettlementShellProfileDefinition.IsStructurallyValid()
|
||||
? SessionConfig.ProviderBillingSettlementShellProfileDefinition
|
||||
: BuildDefaultSpeechProviderBillingSettlementShellProfile(
|
||||
!SessionConfig.ProviderBillingSettlementShellProfileId.IsEmpty()
|
||||
? SessionConfig.ProviderBillingSettlementShellProfileId
|
||||
: TEXT("speech-provider-billing-settlement-shell-v1")
|
||||
);
|
||||
|
||||
FHyperTwistSpeechProviderBillingSettlementShellState ShellState;
|
||||
ShellState.BillingSettlementShellProfileId = Profile.BillingSettlementShellProfileId;
|
||||
ShellState.ActiveProviderProfileId = SessionConfig.ProviderProfileDefinition.IsStructurallyValid()
|
||||
? SessionConfig.ProviderProfileDefinition.ProviderProfileId
|
||||
: TEXT("speech-provider/local-http-sidecar-profile-v1");
|
||||
ShellState.ActiveProviderDisplayLabel = SessionConfig.ProviderProfileDefinition.IsStructurallyValid()
|
||||
? SessionConfig.ProviderProfileDefinition.DisplayLabel
|
||||
: TEXT("Local HTTP Speech Sidecar");
|
||||
ShellState.ActiveServiceLaneId = !SessionConfig.OrchestrationProfile.ServiceLaneId.IsEmpty()
|
||||
? SessionConfig.OrchestrationProfile.ServiceLaneId
|
||||
: TEXT("speech/python-service");
|
||||
ShellState.SelectedSettlementWindowId = !SessionConfig.CostEventTemplate.AggregationWindowId.IsEmpty()
|
||||
? SessionConfig.CostEventTemplate.AggregationWindowId
|
||||
: TEXT("current-session");
|
||||
ShellState.SettlementSurfaceModeId = Profile.SettlementSurfaceMode;
|
||||
ShellState.InvoiceReconciliationModeId = Profile.InvoiceReconciliationMode;
|
||||
ShellState.bSettlementTimelineVisible = Profile.bSupportsSettlementTimeline;
|
||||
ShellState.bInvoiceReconciliationVisible = Profile.bSupportsInvoiceReconciliation;
|
||||
ShellState.bSettlementReadinessVisible = Profile.bSupportsSettlementReadinessReview;
|
||||
for (const FHyperTwistSpeechShellActionBinding& ActionBinding : Profile.ActionBindings)
|
||||
{
|
||||
ShellState.AvailableActionIds.AddUnique(ActionBinding.ActionId);
|
||||
}
|
||||
|
||||
if (PriorState != nullptr
|
||||
&& PriorState->ProviderBillingSettlementShellState.BillingSettlementShellProfileId
|
||||
== Profile.BillingSettlementShellProfileId)
|
||||
{
|
||||
ShellState = PriorState->ProviderBillingSettlementShellState;
|
||||
ShellState.BillingSettlementShellProfileId = Profile.BillingSettlementShellProfileId;
|
||||
ShellState.SettlementSurfaceModeId = Profile.SettlementSurfaceMode;
|
||||
ShellState.InvoiceReconciliationModeId = Profile.InvoiceReconciliationMode;
|
||||
}
|
||||
|
||||
return ShellState;
|
||||
}
|
||||
|
||||
FHyperTwistVisionCorrectionState BuildClassicCubeCorrectionState(
|
||||
const FHyperTwistVisionSessionConfig& SessionConfig,
|
||||
const FHyperTwistVisionReconstructionSession& ReconstructionSession,
|
||||
|
|
@ -11101,6 +11600,11 @@ bool UHyperTwistTrainingSubsystem::OpenActiveCompanionSpeechSession(FString& Out
|
|||
SessionConfig,
|
||||
nullptr
|
||||
);
|
||||
ActiveCompanionSpeechSessionState.ProviderBillingSettlementShellState =
|
||||
HyperTwistTrainingSubsystemInternal::BuildSpeechProviderBillingSettlementShellState(
|
||||
SessionConfig,
|
||||
nullptr
|
||||
);
|
||||
|
||||
if (ActiveCompanionSpeechSessionState.bSessionOpen
|
||||
&& ActiveCompanionSpeechSessionState.ActiveSessionId != SessionConfig.SessionId)
|
||||
|
|
@ -11181,6 +11685,9 @@ FHyperTwistSpeechTranscriptResult UHyperTwistTrainingSubsystem::SubmitActiveComp
|
|||
HyperTwistTrainingSubsystemInternal::SynchronizeSpeechProviderReceiptReviewState(
|
||||
ActiveCompanionSpeechSessionState
|
||||
);
|
||||
HyperTwistTrainingSubsystemInternal::SynchronizeSpeechProviderBillingSettlementState(
|
||||
ActiveCompanionSpeechSessionState
|
||||
);
|
||||
return Result;
|
||||
}
|
||||
|
||||
|
|
@ -11361,6 +11868,9 @@ FHyperTwistSpeechTranscriptResult UHyperTwistTrainingSubsystem::SubmitActiveComp
|
|||
HyperTwistTrainingSubsystemInternal::SynchronizeSpeechProviderReceiptReviewState(
|
||||
ActiveCompanionSpeechSessionState
|
||||
);
|
||||
HyperTwistTrainingSubsystemInternal::SynchronizeSpeechProviderBillingSettlementState(
|
||||
ActiveCompanionSpeechSessionState
|
||||
);
|
||||
|
||||
return Result;
|
||||
}
|
||||
|
|
@ -12113,6 +12623,9 @@ void UHyperTwistTrainingSubsystem::RefreshCompanionSpeechServiceHealth()
|
|||
HyperTwistTrainingSubsystemInternal::SynchronizeSpeechProviderReceiptReviewState(
|
||||
ActiveCompanionSpeechSessionState
|
||||
);
|
||||
HyperTwistTrainingSubsystemInternal::SynchronizeSpeechProviderBillingSettlementState(
|
||||
ActiveCompanionSpeechSessionState
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
@ -12147,6 +12660,9 @@ void UHyperTwistTrainingSubsystem::RefreshCompanionSpeechServiceHealth()
|
|||
HyperTwistTrainingSubsystemInternal::SynchronizeSpeechProviderReceiptReviewState(
|
||||
ActiveCompanionSpeechSessionState
|
||||
);
|
||||
HyperTwistTrainingSubsystemInternal::SynchronizeSpeechProviderBillingSettlementState(
|
||||
ActiveCompanionSpeechSessionState
|
||||
);
|
||||
}
|
||||
|
||||
void UHyperTwistTrainingSubsystem::AppendRecognitionReplayEvent(
|
||||
|
|
|
|||
|
|
@ -3637,6 +3637,405 @@ struct FHyperTwistSpeechProviderReceiptReviewShellState
|
|||
}
|
||||
};
|
||||
|
||||
USTRUCT(BlueprintType)
|
||||
struct FHyperTwistSpeechProviderBillingSettlementShellProfile
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
|
||||
FString BillingSettlementShellProfileId = TEXT("speech-provider-billing-settlement-shell-v1");
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
|
||||
FString ShellKind = TEXT("operator-provider-billing-settlement");
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
|
||||
FString SettlementSurfaceMode = TEXT("posted-charge-settlement-assessment");
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
|
||||
FString InvoiceReconciliationMode = TEXT("estimate-versus-provider-invoice");
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
|
||||
FString RefreshActionId = TEXT("refresh-provider-billing-settlement");
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
|
||||
FString InvoiceReconciliationActionId = TEXT("inspect-provider-invoice-reconciliation");
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
|
||||
FString SettlementReadinessActionId = TEXT("review-provider-settlement-readiness");
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
|
||||
FString RouteInspectActionId = TEXT("inspect-provider-route");
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
|
||||
FString BudgetInspectActionId = TEXT("review-provider-budget");
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
|
||||
int32 RetainedSettlementEntryLimit = 12;
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
|
||||
bool bSupportsSettlementTimeline = true;
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
|
||||
bool bSupportsInvoiceReconciliation = true;
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
|
||||
bool bSupportsSettlementReadinessReview = true;
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
|
||||
bool bSupportsEstimateOnlyFallback = true;
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
|
||||
bool bPreservesFirstPartySettlementOwnership = true;
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
|
||||
TArray<FHyperTwistSpeechShellPanelLayout> Panels;
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
|
||||
TArray<FHyperTwistSpeechShellActionBinding> ActionBindings;
|
||||
|
||||
bool IsStructurallyValid() const
|
||||
{
|
||||
if (BillingSettlementShellProfileId.IsEmpty()
|
||||
|| ShellKind.IsEmpty()
|
||||
|| SettlementSurfaceMode.IsEmpty()
|
||||
|| InvoiceReconciliationMode.IsEmpty()
|
||||
|| RefreshActionId.IsEmpty()
|
||||
|| InvoiceReconciliationActionId.IsEmpty()
|
||||
|| SettlementReadinessActionId.IsEmpty()
|
||||
|| RouteInspectActionId.IsEmpty()
|
||||
|| BudgetInspectActionId.IsEmpty()
|
||||
|| RetainedSettlementEntryLimit <= 0
|
||||
|| Panels.Num() <= 0
|
||||
|| ActionBindings.Num() <= 0)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
for (const FHyperTwistSpeechShellPanelLayout& Panel : Panels)
|
||||
{
|
||||
if (!Panel.IsStructurallyValid())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
for (const FHyperTwistSpeechShellActionBinding& ActionBinding : ActionBindings)
|
||||
{
|
||||
if (!ActionBinding.IsStructurallyValid())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
USTRUCT(BlueprintType)
|
||||
struct FHyperTwistSpeechProviderBillingSettlementEntry
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
|
||||
FString SettlementEntryId;
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
|
||||
FString SourceKind = TEXT("receipt-review-derived-estimate-only");
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
|
||||
FString ReceiptEntryId = TEXT("speech-provider-receipt-entry/current-session");
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
|
||||
FString HistoryEntryId = TEXT("speech-history-entry/current-session");
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
|
||||
FString ProviderProfileId = TEXT("speech-provider/local-http-sidecar-profile-v1");
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
|
||||
FString ServiceLaneId = TEXT("speech/python-service");
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
|
||||
FString PricingSourceId = TEXT("first-party-estimate");
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
|
||||
FString ChargeWindowId = TEXT("current-session");
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
|
||||
FString SettlementWindowId = TEXT("current-session");
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
|
||||
FString CapturedAtUtc = TEXT("2026-05-25T00:00:00Z");
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
|
||||
float EstimatedCostUsd = 0.0f;
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
|
||||
float ReportedCostUsd = 0.0f;
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
|
||||
float SettlementDeltaUsd = 0.0f;
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
|
||||
FString SettlementStateId = TEXT("awaiting-provider-posted-charge");
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
|
||||
FString InvoiceReconciliationStateId = TEXT("invoice-pending");
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
|
||||
bool bHasProviderPostedCharge = false;
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
|
||||
bool bInvoiceReconciliationReady = false;
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
|
||||
bool bRequiresManualReview = false;
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
|
||||
bool bSettlementBlocked = true;
|
||||
|
||||
bool IsStructurallyValid() const
|
||||
{
|
||||
return !SettlementEntryId.IsEmpty()
|
||||
&& !SourceKind.IsEmpty()
|
||||
&& !ReceiptEntryId.IsEmpty()
|
||||
&& !HistoryEntryId.IsEmpty()
|
||||
&& !ProviderProfileId.IsEmpty()
|
||||
&& !ServiceLaneId.IsEmpty()
|
||||
&& !PricingSourceId.IsEmpty()
|
||||
&& !ChargeWindowId.IsEmpty()
|
||||
&& !SettlementWindowId.IsEmpty()
|
||||
&& !CapturedAtUtc.IsEmpty()
|
||||
&& EstimatedCostUsd >= 0.0f
|
||||
&& ReportedCostUsd >= 0.0f
|
||||
&& SettlementDeltaUsd >= 0.0f
|
||||
&& !SettlementStateId.IsEmpty()
|
||||
&& !InvoiceReconciliationStateId.IsEmpty();
|
||||
}
|
||||
};
|
||||
|
||||
USTRUCT(BlueprintType)
|
||||
struct FHyperTwistSpeechProviderBillingSettlementIssue
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
|
||||
FString IssueId;
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
|
||||
FString IssueKind;
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
|
||||
FString StatusLine;
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
|
||||
FString DetailLine;
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
|
||||
FString RecommendedActionId;
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
|
||||
bool bRequiresOperatorAttention = true;
|
||||
|
||||
bool IsStructurallyValid() const
|
||||
{
|
||||
return !IssueId.IsEmpty()
|
||||
&& !IssueKind.IsEmpty()
|
||||
&& !StatusLine.IsEmpty()
|
||||
&& !DetailLine.IsEmpty()
|
||||
&& !RecommendedActionId.IsEmpty();
|
||||
}
|
||||
};
|
||||
|
||||
USTRUCT(BlueprintType)
|
||||
struct FHyperTwistSpeechProviderBillingSettlementShellState
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
|
||||
FString BillingSettlementShellProfileId;
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
|
||||
FString ActiveProviderProfileId = TEXT("speech-provider/local-http-sidecar-profile-v1");
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
|
||||
FString ActiveProviderDisplayLabel = TEXT("Local HTTP Speech Sidecar");
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
|
||||
FString ActiveServiceLaneId = TEXT("speech/python-service");
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
|
||||
FString SelectedSettlementWindowId = TEXT("current-session");
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
|
||||
FString SettlementSurfaceModeId = TEXT("posted-charge-settlement-assessment");
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
|
||||
FString InvoiceReconciliationModeId = TEXT("estimate-versus-provider-invoice");
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
|
||||
FString LatestSettlementEntryId;
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
|
||||
FString LatestSourceKind = TEXT("receipt-review-derived-estimate-only");
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
|
||||
FString StatusLine = TEXT("Provider billing settlement posture is ready for bounded inspection.");
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
|
||||
FString DetailLine = TEXT("First-party settlement posture remains bounded above retained receipt review and does not widen into payment execution.");
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
|
||||
FString SettlementSummaryText;
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
|
||||
int32 SettlementEntryCount = 0;
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
|
||||
int32 ProviderPostedChargeCount = 0;
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
|
||||
int32 ReconciliationReadyEntryCount = 0;
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
|
||||
int32 EstimateOnlyEntryCount = 0;
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
|
||||
int32 ManualReviewEntryCount = 0;
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
|
||||
int32 BlockedSettlementEntryCount = 0;
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
|
||||
float TotalEstimatedCostUsd = 0.0f;
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
|
||||
float TotalReportedCostUsd = 0.0f;
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
|
||||
float TotalSettlementDeltaUsd = 0.0f;
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
|
||||
float HighestSettlementDeltaUsd = 0.0f;
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
|
||||
bool bSettlementTimelineVisible = true;
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
|
||||
bool bInvoiceReconciliationVisible = true;
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
|
||||
bool bSettlementReadinessVisible = true;
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
|
||||
bool bSettlementSummaryReady = false;
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
|
||||
bool bInvoiceReconciliationReady = false;
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
|
||||
bool bSettlementReadinessReviewReady = false;
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
|
||||
int32 ActiveIssueCount = 0;
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
|
||||
FHyperTwistSpeechProviderBillingSettlementIssue ActiveIssue;
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
|
||||
TArray<FHyperTwistSpeechProviderBillingSettlementIssue> Issues;
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
|
||||
TArray<FHyperTwistSpeechProviderBillingSettlementEntry> SettlementEntries;
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
|
||||
TArray<FString> AvailableActionIds;
|
||||
|
||||
bool IsStructurallyValid() const
|
||||
{
|
||||
if (BillingSettlementShellProfileId.IsEmpty()
|
||||
|| ActiveProviderProfileId.IsEmpty()
|
||||
|| ActiveProviderDisplayLabel.IsEmpty()
|
||||
|| ActiveServiceLaneId.IsEmpty()
|
||||
|| SelectedSettlementWindowId.IsEmpty()
|
||||
|| SettlementSurfaceModeId.IsEmpty()
|
||||
|| InvoiceReconciliationModeId.IsEmpty()
|
||||
|| LatestSourceKind.IsEmpty()
|
||||
|| StatusLine.IsEmpty()
|
||||
|| DetailLine.IsEmpty()
|
||||
|| SettlementEntryCount < 0
|
||||
|| ProviderPostedChargeCount < 0
|
||||
|| ReconciliationReadyEntryCount < 0
|
||||
|| EstimateOnlyEntryCount < 0
|
||||
|| ManualReviewEntryCount < 0
|
||||
|| BlockedSettlementEntryCount < 0
|
||||
|| TotalEstimatedCostUsd < 0.0f
|
||||
|| TotalReportedCostUsd < 0.0f
|
||||
|| TotalSettlementDeltaUsd < 0.0f
|
||||
|| HighestSettlementDeltaUsd < 0.0f
|
||||
|| ActiveIssueCount < 0
|
||||
|| AvailableActionIds.Num() <= 0)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (SettlementEntryCount != SettlementEntries.Num()
|
||||
|| ProviderPostedChargeCount > SettlementEntryCount
|
||||
|| ReconciliationReadyEntryCount > SettlementEntryCount
|
||||
|| EstimateOnlyEntryCount > SettlementEntryCount
|
||||
|| ManualReviewEntryCount > SettlementEntryCount
|
||||
|| BlockedSettlementEntryCount > SettlementEntryCount)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (SettlementEntryCount > 0 && LatestSettlementEntryId.IsEmpty())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (bSettlementSummaryReady && SettlementSummaryText.IsEmpty())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (ActiveIssueCount != Issues.Num())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (ActiveIssueCount > 0 && !ActiveIssue.IsStructurallyValid())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
for (const FHyperTwistSpeechProviderBillingSettlementIssue& Issue : Issues)
|
||||
{
|
||||
if (!Issue.IsStructurallyValid())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
for (const FHyperTwistSpeechProviderBillingSettlementEntry& Entry : SettlementEntries)
|
||||
{
|
||||
if (!Entry.IsStructurallyValid())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
for (const FString& ActionId : AvailableActionIds)
|
||||
{
|
||||
if (ActionId.IsEmpty())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
USTRUCT(BlueprintType)
|
||||
struct FHyperTwistSpeechSessionConfig
|
||||
{
|
||||
|
|
@ -3730,6 +4129,14 @@ struct FHyperTwistSpeechSessionConfig
|
|||
FHyperTwistSpeechProviderReceiptReviewShellProfile
|
||||
ProviderReceiptReviewShellProfileDefinition;
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
|
||||
FString ProviderBillingSettlementShellProfileId =
|
||||
TEXT("speech-provider-billing-settlement-shell-v1");
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
|
||||
FHyperTwistSpeechProviderBillingSettlementShellProfile
|
||||
ProviderBillingSettlementShellProfileDefinition;
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
|
||||
FString PayloadCustodyProfileId = TEXT("downloadable-model-payload-custody-v1");
|
||||
|
||||
|
|
@ -3782,6 +4189,8 @@ struct FHyperTwistSpeechSessionConfig
|
|||
|| !UsageCostHistoryExportShellProfileDefinition.IsStructurallyValid()
|
||||
|| ProviderReceiptReviewShellProfileId.IsEmpty()
|
||||
|| !ProviderReceiptReviewShellProfileDefinition.IsStructurallyValid()
|
||||
|| ProviderBillingSettlementShellProfileId.IsEmpty()
|
||||
|| !ProviderBillingSettlementShellProfileDefinition.IsStructurallyValid()
|
||||
|| PayloadCustodyProfileId.IsEmpty()
|
||||
|| !PayloadCustodyProfileDefinition.IsStructurallyValid()
|
||||
|| RequiredModelPayloads.Num() <= 0
|
||||
|
|
|
|||
|
|
@ -1949,6 +1949,9 @@ struct FHyperTwistTrainingCompanionSpeechSessionState
|
|||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
|
||||
FHyperTwistSpeechProviderReceiptReviewShellState ProviderReceiptReviewShellState;
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
|
||||
FHyperTwistSpeechProviderBillingSettlementShellState ProviderBillingSettlementShellState;
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
|
||||
FHyperTwistSpeechTranscriptResult LastTranscriptResult;
|
||||
};
|
||||
|
|
|
|||
|
|
@ -0,0 +1,294 @@
|
|||
// Copyright HyperTwist, Inc. All Rights Reserved.
|
||||
|
||||
#include "Misc/AutomationTest.h"
|
||||
|
||||
#include "Engine/GameInstance.h"
|
||||
#include "HyperTwistRecognition/HyperTwistSpeechClient.h"
|
||||
#include "HyperTwistTraining/HyperTwistTrainingSubsystem.h"
|
||||
#include "UObject/UnrealType.h"
|
||||
|
||||
#if WITH_AUTOMATION_TESTS
|
||||
|
||||
namespace HyperTwistWhisperCppPhase6RADTestInternal
|
||||
{
|
||||
FHyperTwistTrainingDeck MakeSpeechDeck()
|
||||
{
|
||||
FHyperTwistTrainingDeck Deck;
|
||||
Deck.DeckId = TEXT("phase6r-ad/provider-billing-settlement");
|
||||
Deck.Title = TEXT("Phase 6R-AD Provider Billing Settlement");
|
||||
Deck.DeliveryModes = {
|
||||
EHyperTwistTrainingDeliveryMode::CoachReviewed
|
||||
};
|
||||
|
||||
FHyperTwistTrainingCase TrainingCase;
|
||||
TrainingCase.CaseId = TEXT("phase6r-ad-case");
|
||||
TrainingCase.PuzzleId = TEXT("cube/3x3x3");
|
||||
TrainingCase.PromptKind = EHyperTwistTrainingPromptKind::Sequence;
|
||||
TrainingCase.PromptLabel = TEXT("Phase 6R-AD Coach Speech");
|
||||
TrainingCase.AllowedDeliveryModes = {
|
||||
EHyperTwistTrainingDeliveryMode::CoachReviewed
|
||||
};
|
||||
Deck.Cases = {TrainingCase};
|
||||
return Deck;
|
||||
}
|
||||
|
||||
void ForceSpeechClientKind(UHyperTwistTrainingSubsystem* TrainingSubsystem, const FString& ClientKind)
|
||||
{
|
||||
if (TrainingSubsystem == nullptr)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (FStrProperty* SpeechClientKindProperty = FindFProperty<FStrProperty>(
|
||||
UHyperTwistTrainingSubsystem::StaticClass(),
|
||||
TEXT("CompanionSpeechClientKind")
|
||||
))
|
||||
{
|
||||
SpeechClientKindProperty->SetPropertyValue_InContainer(TrainingSubsystem, ClientKind);
|
||||
}
|
||||
}
|
||||
|
||||
UHyperTwistTrainingSubsystem* MakeSpeechSubsystem(const FString& SessionId, const FString& ClientKind)
|
||||
{
|
||||
UGameInstance* GameInstance = NewObject<UGameInstance>(GetTransientPackage());
|
||||
if (GameInstance == nullptr)
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
UHyperTwistTrainingSubsystem* TrainingSubsystem = NewObject<UHyperTwistTrainingSubsystem>(GameInstance);
|
||||
if (TrainingSubsystem == nullptr)
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
ForceSpeechClientKind(TrainingSubsystem, ClientKind);
|
||||
const FHyperTwistTrainingRunState RunState = TrainingSubsystem->StartTrainingRunFromDeck(
|
||||
MakeSpeechDeck(),
|
||||
TEXT("phase6r-ad-user"),
|
||||
SessionId,
|
||||
EHyperTwistTrainingDeliveryMode::CoachReviewed
|
||||
);
|
||||
return RunState.IsStructurallyValid() ? TrainingSubsystem : nullptr;
|
||||
}
|
||||
|
||||
UHyperTwistHttpSpeechClient* ResolveHttpSpeechClient(UHyperTwistTrainingSubsystem* TrainingSubsystem)
|
||||
{
|
||||
if (TrainingSubsystem == nullptr)
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
FString CloseError;
|
||||
TrainingSubsystem->CloseActiveCompanionSpeechSession(CloseError);
|
||||
|
||||
if (FObjectPropertyBase* ClientProperty = FindFProperty<FObjectPropertyBase>(
|
||||
UHyperTwistTrainingSubsystem::StaticClass(),
|
||||
TEXT("ActiveCompanionSpeechClientObject")
|
||||
))
|
||||
{
|
||||
return Cast<UHyperTwistHttpSpeechClient>(
|
||||
ClientProperty->GetObjectPropertyValue_InContainer(TrainingSubsystem)
|
||||
);
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
FHyperTwistSpeechUtteranceEnvelope MakeUtterance()
|
||||
{
|
||||
FHyperTwistSpeechUtteranceEnvelope Utterance;
|
||||
Utterance.AudioRef = TEXT("ready-coach");
|
||||
Utterance.SpeechStartMs = 120;
|
||||
Utterance.SpeechEndMs = 840;
|
||||
Utterance.SilenceGapMs = 240;
|
||||
return Utterance;
|
||||
}
|
||||
|
||||
bool ContainsPanelId(
|
||||
const TArray<FHyperTwistSpeechShellPanelLayout>& Panels,
|
||||
const FString& ExpectedPanelId
|
||||
)
|
||||
{
|
||||
for (const FHyperTwistSpeechShellPanelLayout& Panel : Panels)
|
||||
{
|
||||
if (Panel.PanelId == ExpectedPanelId)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool ContainsActionId(
|
||||
const TArray<FHyperTwistSpeechShellActionBinding>& ActionBindings,
|
||||
const FString& ExpectedActionId
|
||||
)
|
||||
{
|
||||
for (const FHyperTwistSpeechShellActionBinding& ActionBinding : ActionBindings)
|
||||
{
|
||||
if (ActionBinding.ActionId == ExpectedActionId)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
IMPLEMENT_SIMPLE_AUTOMATION_TEST(
|
||||
FHyperTwistWhisperCppPhase6RADProviderBillingSettlementSessionConfigTest,
|
||||
"HyperTwist.Permissive.WhisperCpp.Phase6R.AD.ProviderBillingSettlementSessionConfig",
|
||||
EAutomationTestFlags::EditorContext | EAutomationTestFlags::EngineFilter
|
||||
)
|
||||
|
||||
bool FHyperTwistWhisperCppPhase6RADProviderBillingSettlementSessionConfigTest::RunTest(
|
||||
const FString& Parameters
|
||||
)
|
||||
{
|
||||
UHyperTwistTrainingSubsystem* TrainingSubsystem =
|
||||
HyperTwistWhisperCppPhase6RADTestInternal::MakeSpeechSubsystem(
|
||||
TEXT("phase6r-ad-config-session"),
|
||||
TEXT("mock")
|
||||
);
|
||||
TestNotNull(TEXT("The speech subsystem must be constructed for Phase 6R-AD."), TrainingSubsystem);
|
||||
if (TrainingSubsystem == nullptr)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
FString OpenError;
|
||||
TestTrue(TEXT("The mock billing-settlement route must open a speech session."), TrainingSubsystem->OpenActiveCompanionSpeechSession(OpenError));
|
||||
TestTrue(TEXT("Opening the mock billing-settlement speech session must not report an error."), OpenError.IsEmpty());
|
||||
|
||||
const FHyperTwistSpeechSessionConfig& Config =
|
||||
TrainingSubsystem->GetActiveCompanionSpeechSessionState().SessionConfig;
|
||||
|
||||
TestTrue(TEXT("The billing-settlement session config must remain structurally valid."), Config.IsStructurallyValid());
|
||||
TestEqual(TEXT("The billing-settlement shell profile id must be present."), Config.ProviderBillingSettlementShellProfileId, TEXT("speech-provider-billing-settlement-shell-v1"));
|
||||
TestTrue(TEXT("The billing-settlement shell profile must remain structurally valid."), Config.ProviderBillingSettlementShellProfileDefinition.IsStructurallyValid());
|
||||
TestEqual(TEXT("The billing-settlement shell must preserve the operator-facing shell kind."), Config.ProviderBillingSettlementShellProfileDefinition.ShellKind, TEXT("operator-provider-billing-settlement"));
|
||||
TestEqual(TEXT("The billing-settlement shell must preserve the invoice reconciliation action id."), Config.ProviderBillingSettlementShellProfileDefinition.InvoiceReconciliationActionId, TEXT("inspect-provider-invoice-reconciliation"));
|
||||
TestEqual(TEXT("The billing-settlement shell must preserve the readiness review action id."), Config.ProviderBillingSettlementShellProfileDefinition.SettlementReadinessActionId, TEXT("review-provider-settlement-readiness"));
|
||||
TestTrue(TEXT("The billing-settlement shell must include the settlement timeline panel."), HyperTwistWhisperCppPhase6RADTestInternal::ContainsPanelId(Config.ProviderBillingSettlementShellProfileDefinition.Panels, TEXT("settlement-timeline-card")));
|
||||
TestTrue(TEXT("The billing-settlement shell must include the invoice reconciliation action."), HyperTwistWhisperCppPhase6RADTestInternal::ContainsActionId(Config.ProviderBillingSettlementShellProfileDefinition.ActionBindings, TEXT("inspect-provider-invoice-reconciliation")));
|
||||
return true;
|
||||
}
|
||||
|
||||
IMPLEMENT_SIMPLE_AUTOMATION_TEST(
|
||||
FHyperTwistWhisperCppPhase6RADProviderBillingSettlementShellStateTest,
|
||||
"HyperTwist.Permissive.WhisperCpp.Phase6R.AD.ProviderBillingSettlementShellState",
|
||||
EAutomationTestFlags::EditorContext | EAutomationTestFlags::EngineFilter
|
||||
)
|
||||
|
||||
bool FHyperTwistWhisperCppPhase6RADProviderBillingSettlementShellStateTest::RunTest(
|
||||
const FString& Parameters
|
||||
)
|
||||
{
|
||||
UHyperTwistTrainingSubsystem* TrainingSubsystem =
|
||||
HyperTwistWhisperCppPhase6RADTestInternal::MakeSpeechSubsystem(
|
||||
TEXT("phase6r-ad-shell-session"),
|
||||
TEXT("mock")
|
||||
);
|
||||
TestNotNull(TEXT("The speech subsystem must be constructed for Phase 6R-AD."), TrainingSubsystem);
|
||||
if (TrainingSubsystem == nullptr)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
FString OpenError;
|
||||
TestTrue(TEXT("The mock billing-settlement route must open a speech session."), TrainingSubsystem->OpenActiveCompanionSpeechSession(OpenError));
|
||||
TestTrue(TEXT("Opening the mock billing-settlement speech session must not report an error."), OpenError.IsEmpty());
|
||||
|
||||
const FHyperTwistSpeechTranscriptResult Result =
|
||||
TrainingSubsystem->SubmitActiveCompanionSpeechUtterance(
|
||||
HyperTwistWhisperCppPhase6RADTestInternal::MakeUtterance()
|
||||
);
|
||||
TestTrue(TEXT("The mock billing-settlement utterance must produce a final transcript."), Result.bIsFinal);
|
||||
TestTrue(TEXT("The mock billing-settlement utterance must remain structurally valid."), Result.IsStructurallyValid());
|
||||
|
||||
const FHyperTwistTrainingCompanionSpeechSessionState SessionState =
|
||||
TrainingSubsystem->GetActiveCompanionSpeechSessionState();
|
||||
const FHyperTwistSpeechServiceHealth& Health = SessionState.ServiceHealth;
|
||||
const FHyperTwistSpeechProviderBillingSettlementShellState& Settlement =
|
||||
SessionState.ProviderBillingSettlementShellState;
|
||||
|
||||
TestTrue(TEXT("The service health must expose the billing-settlement shell."), Health.Capabilities.Contains(TEXT("providerBillingSettlementShell")));
|
||||
TestTrue(TEXT("The service health must expose invoice reconciliation."), Health.Capabilities.Contains(TEXT("providerInvoiceReconciliation")));
|
||||
TestTrue(TEXT("The service health must expose settlement readiness review."), Health.Capabilities.Contains(TEXT("providerSettlementReadinessReview")));
|
||||
|
||||
TestTrue(TEXT("The billing-settlement shell state must remain structurally valid."), Settlement.IsStructurallyValid());
|
||||
TestEqual(TEXT("The billing-settlement shell must preserve the mock provider profile."), Settlement.ActiveProviderProfileId, TEXT("speech-provider/mock-offline-profile-v1"));
|
||||
TestEqual(TEXT("The billing-settlement shell must retain exactly two bounded settlement entries after open and submit."), Settlement.SettlementEntryCount, 2);
|
||||
TestEqual(TEXT("The billing-settlement shell must preserve the latest source kind."), Settlement.LatestSourceKind, TEXT("receipt-review-derived-estimate-only"));
|
||||
TestTrue(TEXT("The billing-settlement shell must preserve summary readiness."), Settlement.bSettlementSummaryReady);
|
||||
TestFalse(TEXT("The billing-settlement shell must not mark invoice reconciliation ready before provider-posted charges appear."), Settlement.bInvoiceReconciliationReady);
|
||||
TestTrue(TEXT("The billing-settlement shell must preserve readiness-review visibility."), Settlement.bSettlementReadinessReviewReady);
|
||||
TestTrue(TEXT("The billing-settlement shell must preserve the invoice reconciliation action."), Settlement.AvailableActionIds.Contains(TEXT("inspect-provider-invoice-reconciliation")));
|
||||
TestTrue(TEXT("The billing-settlement shell must preserve the readiness review action."), Settlement.AvailableActionIds.Contains(TEXT("review-provider-settlement-readiness")));
|
||||
TestEqual(TEXT("The billing-settlement shell must preserve zero provider-posted charges in the mock route."), Settlement.ProviderPostedChargeCount, 0);
|
||||
TestEqual(TEXT("The billing-settlement shell must preserve zero reconciliation-ready entries in the mock route."), Settlement.ReconciliationReadyEntryCount, 0);
|
||||
TestEqual(TEXT("The billing-settlement shell must preserve the estimate-only entry count."), Settlement.EstimateOnlyEntryCount, 2);
|
||||
TestEqual(TEXT("The billing-settlement shell must preserve the blocked-settlement entry count."), Settlement.BlockedSettlementEntryCount, 2);
|
||||
TestTrue(TEXT("The billing-settlement shell must accumulate the mock estimated cost."), FMath::IsNearlyEqual(Settlement.TotalEstimatedCostUsd, 0.02f));
|
||||
TestTrue(TEXT("The billing-settlement shell must preserve zero settlement delta while no provider charge is posted."), FMath::IsNearlyEqual(Settlement.TotalSettlementDeltaUsd, 0.0f));
|
||||
TestEqual(TEXT("The billing-settlement shell must surface the pending provider-charge issue."), Settlement.ActiveIssue.IssueKind, TEXT("provider-charge-pending"));
|
||||
TestTrue(TEXT("The billing-settlement summary text must preserve the bounded reconciliation-ready count."), Settlement.SettlementSummaryText.Contains(TEXT("reconciliationReadyEntryCount=0")));
|
||||
return true;
|
||||
}
|
||||
|
||||
IMPLEMENT_SIMPLE_AUTOMATION_TEST(
|
||||
FHyperTwistWhisperCppPhase6RADProviderBackedBillingSettlementFallbackTest,
|
||||
"HyperTwist.Permissive.WhisperCpp.Phase6R.AD.ProviderBackedBillingSettlementFallback",
|
||||
EAutomationTestFlags::EditorContext | EAutomationTestFlags::EngineFilter
|
||||
)
|
||||
|
||||
bool FHyperTwistWhisperCppPhase6RADProviderBackedBillingSettlementFallbackTest::RunTest(
|
||||
const FString& Parameters
|
||||
)
|
||||
{
|
||||
UHyperTwistTrainingSubsystem* TrainingSubsystem =
|
||||
HyperTwistWhisperCppPhase6RADTestInternal::MakeSpeechSubsystem(
|
||||
TEXT("phase6r-ad-provider-session"),
|
||||
TEXT("http-sidecar")
|
||||
);
|
||||
TestNotNull(TEXT("The speech subsystem must be constructed for Phase 6R-AD."), TrainingSubsystem);
|
||||
if (TrainingSubsystem == nullptr)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
UHyperTwistHttpSpeechClient* HttpSpeechClient =
|
||||
HyperTwistWhisperCppPhase6RADTestInternal::ResolveHttpSpeechClient(TrainingSubsystem);
|
||||
TestNotNull(TEXT("The provider-backed speech client must be available for the billing-settlement fallback test."), HttpSpeechClient);
|
||||
if (HttpSpeechClient == nullptr)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
HttpSpeechClient->ServiceBaseUrl.Reset();
|
||||
|
||||
FString OpenError;
|
||||
TestFalse(TEXT("The provider-backed billing-settlement path must fail cleanly when no provider endpoint is configured."), TrainingSubsystem->OpenActiveCompanionSpeechSession(OpenError));
|
||||
TestEqual(TEXT("The provider-backed billing-settlement path must report the missing service base URL."), OpenError, TEXT("service-base-url-missing"));
|
||||
|
||||
const FHyperTwistTrainingCompanionSpeechSessionState SessionState =
|
||||
TrainingSubsystem->GetActiveCompanionSpeechSessionState();
|
||||
const FHyperTwistSpeechProviderBillingSettlementShellState& Settlement =
|
||||
SessionState.ProviderBillingSettlementShellState;
|
||||
|
||||
TestTrue(TEXT("The provider-backed billing-settlement fallback must remain structurally valid."), Settlement.IsStructurallyValid());
|
||||
TestEqual(TEXT("The fallback billing-settlement shell must preserve the sidecar profile id."), Settlement.ActiveProviderProfileId, TEXT("speech-provider/local-http-sidecar-profile-v1"));
|
||||
TestEqual(TEXT("The fallback billing-settlement shell must preserve the fallback source kind."), Settlement.LatestSourceKind, TEXT("receipt-review-fallback-health"));
|
||||
TestEqual(TEXT("The fallback billing-settlement shell must preserve one settlement entry."), Settlement.SettlementEntryCount, 1);
|
||||
TestEqual(TEXT("The fallback billing-settlement shell must surface one active issue."), Settlement.ActiveIssueCount, 1);
|
||||
TestEqual(TEXT("The fallback billing-settlement shell must surface the route-degraded issue kind."), Settlement.ActiveIssue.IssueKind, TEXT("route-degraded"));
|
||||
TestTrue(TEXT("The fallback billing-settlement shell must still expose summary readiness for bounded inspection output."), Settlement.bSettlementSummaryReady);
|
||||
TestFalse(TEXT("The fallback billing-settlement shell must preserve invoice reconciliation as not ready."), Settlement.bInvoiceReconciliationReady);
|
||||
TestTrue(TEXT("The fallback billing-settlement summary text must preserve the missing-endpoint reason."), Settlement.SettlementSummaryText.Contains(TEXT("service-base-url-missing")));
|
||||
return true;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
|
@ -203,8 +203,12 @@ Status update on `2026-05-21`:
|
|||
inspection control pass is now consumed
|
||||
- the bounded first-party `Phase 6R-AC` provider receipt review and posted-charge inspection
|
||||
packet is now landed in current code
|
||||
- the current next bounded move is a source-backed `Phase 6R-AD` first-party provider billing
|
||||
settlement and invoice reconciliation preparation/control pass, not a new restrictive packet by
|
||||
- the generic source-backed `Phase 6R-AD` first-party provider billing settlement and invoice
|
||||
reconciliation control pass is now consumed
|
||||
- the bounded first-party `Phase 6R-AD` provider billing settlement and invoice reconciliation
|
||||
packet is now landed in current code
|
||||
- the current next bounded move is a source-backed `Phase 6R-AE` first-party provider settlement
|
||||
exception and external-portal handoff preparation/control pass, not a new restrictive packet by
|
||||
default
|
||||
- use the repo-row README census, the portfolio standing refresh backfill, and the `2R-A`
|
||||
ownership contract for the current queue after that correction
|
||||
|
|
@ -354,8 +358,8 @@ Current practical interpretation:
|
|||
- the landed `PostHog/posthog` boundary-sensitive widening packet now lives in `docs/HYPERTWIST_PHASE_4R_PACKET_4R_D_POSTHOG_CONTROL_PLANE_IMPLEMENTATION_2026-05-13.md`
|
||||
- the landed `screenpipe/screenpipe` boundary-sensitive widening packet now lives in `docs/HYPERTWIST_PHASE_4R_PACKET_4R_E_SCREENPIPE_CAPTURE_HISTORY_IMPLEMENTATION_2026-05-13.md`
|
||||
- the landed `remotion-dev/remotion` boundary-sensitive widening packet now lives in `docs/HYPERTWIST_PHASE_4R_PACKET_4R_F_REMOTION_MEDIA_EXPORT_IMPLEMENTATION_2026-05-13.md`
|
||||
- the next bounded move is a source-backed `Phase 6R-AD` first-party provider billing settlement
|
||||
and invoice reconciliation preparation/control pass
|
||||
- the next bounded move is a source-backed `Phase 6R-AE` first-party provider settlement
|
||||
exception and external-portal handoff preparation/control pass
|
||||
|
||||
Companion docs:
|
||||
|
||||
|
|
@ -1621,11 +1625,15 @@ Approved working posture:
|
|||
- the next bounded first-party implementation slice is now landed as normalized usage/cost
|
||||
event-model metadata above the existing transcript-session, shell, payload-custody,
|
||||
provider-custody, and provider-routing seams
|
||||
- the next bounded first-party implementation slice is now landed as provider billing settlement
|
||||
and invoice reconciliation shell metadata above the existing transcript-session, shell,
|
||||
payload-custody, provider-custody, provider-routing, usage/cost, history/export, and
|
||||
receipt-review seams
|
||||
- the top-level provider/session contract remains first-party HyperTwist-owned and
|
||||
provider-neutral; this row does not own that lane
|
||||
- keep operator-facing provider dashboards, real device-permission workflow, native
|
||||
capture-route ownership, actual payload shipping, and future voice-asset review separate from
|
||||
the code-license judgment
|
||||
- keep provider settlement exception/handoff, real device-permission workflow, native
|
||||
capture-route ownership, actual payment execution, provider-portal ownership, actual payload
|
||||
shipping, and future voice-asset review separate from the code-license judgment
|
||||
- treat `SYSTRAN/faster-whisper` as the landed complementary Python-orchestration donor rather
|
||||
than widening `whisper.cpp` into the runtime core
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,177 @@
|
|||
# HyperTwist Phase 6R-AD first-party provider billing settlement and invoice reconciliation implementation packet
|
||||
|
||||
Created on `2026-05-25`
|
||||
|
||||
## Status
|
||||
|
||||
- first-party HyperTwist packet
|
||||
- bounded `Phase 6R-AD` implementation slice
|
||||
|
||||
## Purpose
|
||||
|
||||
This packet lands the next bounded first-party slice above the landed speech
|
||||
session, microphone-shell, payload-custody, provider-custody,
|
||||
provider-routing, normalized usage/cost event, operator-facing
|
||||
usage/cost dashboard, provider usage/cost history/export, and provider
|
||||
receipt-review seams.
|
||||
|
||||
The landed slice is:
|
||||
|
||||
- first-party provider billing settlement and invoice reconciliation shell
|
||||
|
||||
It is not:
|
||||
|
||||
- an actual payment execution packet
|
||||
- a provider-portal ownership packet
|
||||
- an external billing settlement packet
|
||||
- an external invoice remittance packet
|
||||
- an actual payload-shipping packet
|
||||
- a real device-permission workflow packet
|
||||
- a native audio-device route ownership packet
|
||||
- a broad assistant-platform packet
|
||||
|
||||
## Current authority basis
|
||||
|
||||
This implementation packet stands on:
|
||||
|
||||
- `docs/ops/HYPERTWIST_PROVIDER_NEUTRALITY_AND_BYOK_DOCTRINE_2026-05-21.md`
|
||||
- `docs/arch/HYPERTWIST_PHASE6R_AD_FIRST_PARTY_PROVIDER_BILLING_SETTLEMENT_AND_INVOICE_RECONCILIATION_PREPARATION_PACKET_2026-05-25.md`
|
||||
- `docs/arch/HYPERTWIST_PHASE6R_AC_FIRST_PARTY_PROVIDER_RECEIPT_REVIEW_AND_POSTED_CHARGE_INSPECTION_IMPLEMENTATION_PACKET_2026-05-25.md`
|
||||
|
||||
The preserved owners do not change:
|
||||
|
||||
- first-party HyperTwist remains the top-level provider/session owner
|
||||
- first-party HyperTwist remains the usage/cost governance owner
|
||||
- first-party HyperTwist remains the settlement and reconciliation shell owner
|
||||
- `ggml-org/whisper.cpp` remains bounded to offline STT donor seams
|
||||
- `SYSTRAN/faster-whisper` remains bounded to complementary Python
|
||||
orchestration/service-lane seams
|
||||
|
||||
## Landed scope
|
||||
|
||||
The current code now owns a bounded settlement seam through:
|
||||
|
||||
- retained recognition contract types for:
|
||||
- `FHyperTwistSpeechProviderBillingSettlementShellProfile`
|
||||
- `FHyperTwistSpeechProviderBillingSettlementEntry`
|
||||
- `FHyperTwistSpeechProviderBillingSettlementIssue`
|
||||
- `FHyperTwistSpeechProviderBillingSettlementShellState`
|
||||
- expanded `FHyperTwistSpeechSessionConfig`
|
||||
- expanded `FHyperTwistTrainingCompanionSpeechSessionState`
|
||||
- contract-library defaults for:
|
||||
- settlement shell profile ids and definitions
|
||||
- settlement panel ids and action bindings
|
||||
- service-health capability exposure
|
||||
- active companion session-config, service-health, and derived settlement shell
|
||||
composition in:
|
||||
- `UHyperTwistTrainingSubsystem`
|
||||
- direct speech-client health capability exposure in:
|
||||
- `UHyperTwistHttpSpeechClient`
|
||||
- `UHyperTwistMockSpeechClient`
|
||||
- focused automation coverage in:
|
||||
- `HyperTwistWhisperCppPhase6RADProviderBillingSettlementShellContractTest.cpp`
|
||||
|
||||
## Why this is still intentionally bounded
|
||||
|
||||
This packet lands the settlement and invoice reconciliation shell family only.
|
||||
|
||||
Still deferred:
|
||||
|
||||
- provider settlement exception and external-portal handoff shell surfaces
|
||||
- actual payment execution or provider-portal ownership
|
||||
- actual downloadable model/payload shipping
|
||||
- real device-permission workflow
|
||||
- native audio-device route ownership beyond bounded shell posture
|
||||
- broad assistant-platform scope
|
||||
|
||||
## Validation
|
||||
|
||||
Build validation:
|
||||
|
||||
- `UnrealHyperTwistEditor Win64 Development`
|
||||
|
||||
Focused automation validation:
|
||||
|
||||
- `HyperTwist.Permissive.WhisperCpp.Phase6R.AD` `3/3`
|
||||
|
||||
Regression automation validation:
|
||||
|
||||
- `HyperTwist.Permissive.WhisperCpp.Phase6R.AC` `3/3`
|
||||
- `HyperTwist.Permissive.WhisperCpp.Phase6R.AB` `3/3`
|
||||
- `HyperTwist.Permissive.WhisperCpp.Phase6R.AA` `3/3`
|
||||
- `HyperTwist.Permissive.WhisperCpp.Phase6R.Z` `3/3`
|
||||
- `HyperTwist.Permissive.WhisperCpp.Phase6R.Y` `3/3`
|
||||
- `HyperTwist.Permissive.WhisperCpp.Phase6R.X` `3/3`
|
||||
- `HyperTwist.Permissive.WhisperCpp.Phase6R.W` `3/3`
|
||||
- `HyperTwist.Permissive.WhisperCpp.Phase6R.V` `3/3`
|
||||
- `HyperTwist.Permissive.WhisperCpp.Phase6R.U` `3/3`
|
||||
- `HyperTwist.Permissive.WhisperCpp.Phase6R.E` `3/3`
|
||||
- `HyperTwist.Permissive.FasterWhisper.Phase6R.F` `3/3`
|
||||
- `HyperTwist.Permissive.Piper.Phase6R.G` `3/3`
|
||||
- `HyperTwist.Permissive.Coqui.Phase6R.H` `3/3`
|
||||
- `HyperTwist.CleanRoom.CubeDesk` `14/14`
|
||||
|
||||
One in-loop `Phase 6R-AA` regression run crashed once while materializing the
|
||||
existing training catalog fixture, but the immediate isolated rerun passed
|
||||
cleanly and the retained named regression log/report ended in success. No code
|
||||
change was required for that transient rerun.
|
||||
|
||||
Non-blocking warnings stayed limited to the existing Unreal headless/editor
|
||||
noise, the pre-existing plugin dependency warning on `UnrealMCP`, and the
|
||||
known `http:/speech/health` and `http:/vision/health` hostname-resolution
|
||||
warnings in provider-backed automation.
|
||||
|
||||
## Queue effect
|
||||
|
||||
This packet consumes the current `Phase 6R-AD` implementation slice.
|
||||
|
||||
`ggml-org/whisper.cpp` remains only partially incorporated:
|
||||
|
||||
- landed now:
|
||||
- speech transcript session boundary
|
||||
- live microphone shell boundary
|
||||
- device-permission and capture-route readiness shell posture
|
||||
- downloadable model and payload custody boundary
|
||||
- first-party provider-profile and BYOK custody boundary
|
||||
- first-party provider routing and workflow-policy boundary
|
||||
- first-party normalized usage/cost event-model boundary
|
||||
- first-party operator-facing provider usage/cost dashboard shell boundary
|
||||
- first-party provider usage/cost history/export shell boundary
|
||||
- first-party provider receipt review and posted-charge inspection shell
|
||||
boundary
|
||||
- first-party provider billing settlement and invoice reconciliation shell
|
||||
boundary for:
|
||||
- settlement shell profile ids and definitions
|
||||
- derived settlement entry and issue/state posture
|
||||
- companion-session settlement composition
|
||||
- capability exposure for:
|
||||
- `providerBillingSettlementShell`
|
||||
- `providerInvoiceReconciliation`
|
||||
- `providerSettlementReadinessReview`
|
||||
- still deferred:
|
||||
- provider settlement exception and external-portal handoff shell surfaces
|
||||
- actual payment execution or provider-portal ownership
|
||||
- actual downloadable model/payload shipping
|
||||
- real device-permission workflow
|
||||
- native audio-device route ownership beyond bounded shell posture
|
||||
- broad assistant-platform scope
|
||||
|
||||
The next clean move is:
|
||||
|
||||
- a source-backed `Phase 6R-AE` first-party provider settlement exception and
|
||||
external-portal handoff preparation/control pass
|
||||
|
||||
Keep the future sequencing guards visible:
|
||||
|
||||
- first-party HyperTwist
|
||||
- keep the top-level provider/session contract, routing/policy lane,
|
||||
usage/cost governance lane, dashboard shell lane, history/export shell
|
||||
lane, receipt-review shell lane, and settlement shell lane first-party
|
||||
- `ggml-org/whisper.cpp`
|
||||
- keep code-license judgments separate from actual model or payload shipping
|
||||
review in any follow-on packet
|
||||
- `SYSTRAN/faster-whisper`
|
||||
- keep the row complementary to the landed provider/session, custody,
|
||||
routing, usage/cost, dashboard, history/export, receipt-review, and
|
||||
settlement seams rather than widening it into payment execution or portal
|
||||
ownership
|
||||
|
|
@ -0,0 +1,137 @@
|
|||
# HyperTwist Phase 6R-AD first-party provider billing settlement and invoice reconciliation preparation packet
|
||||
|
||||
Created on `2026-05-25`
|
||||
|
||||
## Status
|
||||
|
||||
- first-party HyperTwist packet
|
||||
- source-backed `Phase 6R-AD` preparation/control slice
|
||||
|
||||
## Purpose
|
||||
|
||||
This packet scopes the next bounded first-party slice above the landed speech
|
||||
session, microphone-shell, payload-custody, provider-custody,
|
||||
provider-routing, normalized usage/cost event, operator-facing
|
||||
usage/cost dashboard, provider usage/cost history/export, and provider
|
||||
receipt-review seams.
|
||||
|
||||
The granted slice is:
|
||||
|
||||
- first-party provider billing settlement and invoice reconciliation shell only
|
||||
|
||||
It is not:
|
||||
|
||||
- an actual payment execution packet
|
||||
- a provider-portal ownership packet
|
||||
- an external billing settlement packet
|
||||
- an external invoice remittance packet
|
||||
- an actual payload-shipping packet
|
||||
- a real device-permission workflow packet
|
||||
- a native audio-device route ownership packet
|
||||
- a broad assistant-platform packet
|
||||
|
||||
## Current authority basis
|
||||
|
||||
This control pass stands on:
|
||||
|
||||
- `docs/ops/HYPERTWIST_PROVIDER_NEUTRALITY_AND_BYOK_DOCTRINE_2026-05-21.md`
|
||||
- `docs/arch/HYPERTWIST_PHASE6R_AC_FIRST_PARTY_PROVIDER_RECEIPT_REVIEW_AND_POSTED_CHARGE_INSPECTION_IMPLEMENTATION_PACKET_2026-05-25.md`
|
||||
- `docs/v6_5_deep_manual_pack/HyperTwist/ROADMAP.md`
|
||||
|
||||
The preserved owners do not change here:
|
||||
|
||||
- first-party HyperTwist remains the top-level provider/session owner
|
||||
- first-party HyperTwist remains the usage/cost governance owner
|
||||
- first-party HyperTwist remains the settlement and reconciliation shell owner
|
||||
- `ggml-org/whisper.cpp` remains bounded to offline STT donor seams
|
||||
- `SYSTRAN/faster-whisper` remains bounded to complementary Python
|
||||
orchestration/service-lane seams
|
||||
|
||||
## Granted family
|
||||
|
||||
The bounded `Phase 6R-AD` slice may land:
|
||||
|
||||
1. first-party settlement shell profile ids and definitions
|
||||
2. derived settlement entry, issue, and shell-state record shapes
|
||||
3. session-config and companion-session-state expansion for those shapes
|
||||
4. bounded capability exposure for:
|
||||
- `providerBillingSettlementShell`
|
||||
- `providerInvoiceReconciliation`
|
||||
- `providerSettlementReadinessReview`
|
||||
5. focused automation for estimate-only settlement posture and provider-backed
|
||||
fallback settlement posture
|
||||
|
||||
The packet must stay out of:
|
||||
|
||||
- actual payment execution
|
||||
- provider-portal ownership
|
||||
- external settlement ownership
|
||||
- external invoice-remittance ownership
|
||||
- actual payload download or redistribution ownership
|
||||
- real device-permission workflow ownership
|
||||
- native audio-device route ownership
|
||||
|
||||
## Proposed implementation shape
|
||||
|
||||
Land the narrower first-party boundary through:
|
||||
|
||||
- retained recognition contract types for:
|
||||
- provider billing settlement shell profile
|
||||
- provider billing settlement entry
|
||||
- provider billing settlement issue
|
||||
- provider billing settlement shell state
|
||||
- expanded speech session config
|
||||
- expanded companion speech session state
|
||||
- contract-library defaults for:
|
||||
- settlement shell profile ids and definitions
|
||||
- settlement action bindings and panel ids
|
||||
- service-health capability exposure
|
||||
- training-subsystem composition for:
|
||||
- bounded settlement posture derived from retained receipt-review snapshots
|
||||
- invoice reconciliation readiness posture
|
||||
- settlement readiness review posture
|
||||
- route-degraded and provider-charge-pending issue posture
|
||||
- direct speech-client health capability exposure for:
|
||||
- `providerBillingSettlementShell`
|
||||
- `providerInvoiceReconciliation`
|
||||
- `providerSettlementReadinessReview`
|
||||
|
||||
## Validation target
|
||||
|
||||
Validate with:
|
||||
|
||||
- Unreal build for `UnrealHyperTwistEditor Win64 Development`
|
||||
- focused automation:
|
||||
- `HyperTwist.Permissive.WhisperCpp.Phase6R.AD`
|
||||
- regressions:
|
||||
- `HyperTwist.Permissive.WhisperCpp.Phase6R.AC`
|
||||
- `HyperTwist.Permissive.WhisperCpp.Phase6R.AB`
|
||||
- `HyperTwist.Permissive.WhisperCpp.Phase6R.AA`
|
||||
- `HyperTwist.Permissive.WhisperCpp.Phase6R.Z`
|
||||
- `HyperTwist.Permissive.WhisperCpp.Phase6R.Y`
|
||||
- `HyperTwist.Permissive.WhisperCpp.Phase6R.X`
|
||||
- `HyperTwist.Permissive.WhisperCpp.Phase6R.W`
|
||||
- `HyperTwist.Permissive.WhisperCpp.Phase6R.V`
|
||||
- `HyperTwist.Permissive.WhisperCpp.Phase6R.U`
|
||||
- `HyperTwist.Permissive.WhisperCpp.Phase6R.E`
|
||||
- `HyperTwist.Permissive.FasterWhisper.Phase6R.F`
|
||||
- `HyperTwist.Permissive.Piper.Phase6R.G`
|
||||
- `HyperTwist.Permissive.Coqui.Phase6R.H`
|
||||
- `HyperTwist.CleanRoom.CubeDesk`
|
||||
|
||||
## Queue effect
|
||||
|
||||
If this packet lands cleanly, the next bounded move becomes:
|
||||
|
||||
- a source-backed `Phase 6R-AE` first-party provider settlement exception and
|
||||
external-portal handoff preparation/control pass
|
||||
|
||||
Keep the narrow sequencing guards visible:
|
||||
|
||||
- keep settlement and reconciliation shell ownership separate from actual
|
||||
payment execution
|
||||
- keep settlement exception handling and external-portal handoff separate from
|
||||
this packet
|
||||
- keep actual payload shipping separate from usage/cost governance
|
||||
- keep real device-permission workflow and native capture-route ownership
|
||||
outside this packet
|
||||
|
|
@ -180,15 +180,19 @@ The next bounded move is now:
|
|||
posted-charge inspection control pass is now consumed
|
||||
32. the bounded first-party `Phase 6R-AC` provider receipt review and posted-charge inspection
|
||||
packet is now landed in current code
|
||||
33. the next bounded move is a source-backed `Phase 6R-AD` first-party provider billing
|
||||
settlement and invoice reconciliation preparation/control pass
|
||||
34. keep the speech-lane guard visible:
|
||||
33. the generic source-backed `Phase 6R-AD` first-party provider billing settlement and
|
||||
invoice reconciliation control pass is now consumed
|
||||
34. the bounded first-party `Phase 6R-AD` provider billing settlement and invoice
|
||||
reconciliation packet is now landed in current code
|
||||
35. the next bounded move is a source-backed `Phase 6R-AE` first-party provider settlement
|
||||
exception and external-portal handoff preparation/control pass
|
||||
36. keep the speech-lane guard visible:
|
||||
- keep code-license judgments separate from model, voice, and payload-license review
|
||||
35. keep the provider-neutral speech-lane guard visible:
|
||||
37. keep the provider-neutral speech-lane guard visible:
|
||||
- first-party HyperTwist owns the top-level provider/session contract
|
||||
- `whisper.cpp` and `faster-whisper` may win narrower donor slices without inheriting that
|
||||
lane, the provider routing/policy lane, or the usage/cost governance lane
|
||||
36. keep the `MagicTile` guard visible:
|
||||
38. keep the `MagicTile` guard visible:
|
||||
- keep broad non-Euclidean interaction or WinForms/OpenTK host-shell ownership closed by
|
||||
default unless a narrower first-party gap is proven above the landed macro-remapping seam
|
||||
|
||||
|
|
|
|||
|
|
@ -220,8 +220,10 @@ Current landed ordering status:
|
|||
first-party implementation anchor
|
||||
- provider receipt review and posted-charge inspection now also have a bounded
|
||||
first-party implementation anchor
|
||||
- provider billing settlement and invoice reconciliation remain the next
|
||||
bounded first-party gap above the landed receipt-review shell
|
||||
- provider billing settlement and invoice reconciliation now also have a
|
||||
bounded first-party implementation anchor
|
||||
- provider settlement exception and external-portal handoff remain the next
|
||||
bounded first-party gap above the landed settlement shell
|
||||
- actual payment execution or provider-portal ownership remains later
|
||||
first-party work
|
||||
- provider-specific adapters remain later work
|
||||
|
|
|
|||
|
|
@ -129,6 +129,7 @@ Do not collapse those three tiers into one undifferentiated "features" voice.
|
|||
| First-party operator-facing provider usage/cost dashboard shell | Implemented now | landed first-party `Phase 6R-AA` | First-party dashboard shell profiles, dashboard issue/state objects, quota escalation banners, route diagnostics, and operator-facing panel/action contracts are now live above the landed normalized usage/cost seam. |
|
||||
| First-party provider usage/cost history/export shell | Implemented now | landed first-party `Phase 6R-AB` | First-party rolling history entries, export issue/state objects, summary export preview/copy posture, and operator-facing panel/action contracts are now live above the landed provider usage/cost dashboard seam. |
|
||||
| First-party provider receipt review and posted-charge inspection shell | Implemented now | landed first-party `Phase 6R-AC` | First-party receipt-review shell profiles, derived receipt-review entries, charge-pending and variance issue posture, and bounded inspection capability exposure are now live above the landed provider usage/cost history/export seam. |
|
||||
| First-party provider billing settlement and invoice reconciliation shell | Implemented now | landed first-party `Phase 6R-AD` | First-party settlement shell profiles, derived settlement entries, invoice-reconciliation posture, settlement-readiness issue state, and bounded settlement capability exposure are now live above the landed provider receipt-review seam. |
|
||||
| Analytics/reporting surfaces | Implemented now | landed analytics/reporting packets | Reporting is real, but bounded to accepted retained slices. |
|
||||
| Browser/spatial/media adjunct surfaces | Implemented now | landed `three.js`, `react-three-fiber`, `xr`, `model-viewer`, `remotion` packets | These are implemented bounded families, not proof of unlimited browser-shell parity. |
|
||||
|
||||
|
|
@ -227,6 +228,7 @@ repo.
|
|||
| First-party operator-facing provider usage/cost dashboard shell | Implemented now | landed first-party `Phase 6R-AA` packet | Current bounded dashboard shell profile ids/definitions, dashboard issue/state posture, and capability exposure above the landed normalized usage/cost seam. |
|
||||
| First-party provider usage/cost history/export shell | Implemented now | landed first-party `Phase 6R-AB` packet | Current bounded history/export shell profile ids/definitions, rolling history entry and export issue/state posture, summary export preview/copy behavior, and capability exposure above the landed provider usage/cost dashboard seam. |
|
||||
| First-party provider receipt review and posted-charge inspection shell | Implemented now | landed first-party `Phase 6R-AC` packet | Current bounded receipt-review shell profile ids/definitions, derived receipt-review entry and issue/state posture, and capability exposure above the landed provider usage/cost history/export seam. |
|
||||
| First-party provider billing settlement and invoice reconciliation shell | Implemented now | landed first-party `Phase 6R-AD` packet | Current bounded settlement shell profile ids/definitions, derived settlement entry and issue/state posture, invoice-reconciliation readiness, and capability exposure above the landed provider receipt-review seam. |
|
||||
| OpenAI-compatible custom endpoint support | Deep-source grounded retained | doctrine-defined first-party target | First-class target is now represented in the landed custody model, but full routing/runtime support is still deferred. |
|
||||
| Provider-specific overlays | Shallow placeholder | future provider-family work only | Keep internal until source-grounded and normalized. |
|
||||
|
||||
|
|
|
|||
|
|
@ -179,10 +179,14 @@ Canonical discovery surfaces for roadmap interpretation:
|
|||
inspection control pass is now consumed
|
||||
- the bounded first-party `Phase 6R-AC` provider receipt review and posted-charge inspection
|
||||
packet is now landed in current code
|
||||
- the current next bounded move is a source-backed `Phase 6R-AD` first-party provider billing
|
||||
settlement and invoice reconciliation preparation/control pass, while keeping actual payment
|
||||
execution, real device-permission workflow, native capture-route ownership, and actual payload
|
||||
shipping separately deferred
|
||||
- the generic source-backed `Phase 6R-AD` first-party provider billing settlement and invoice
|
||||
reconciliation control pass is now consumed
|
||||
- the bounded first-party `Phase 6R-AD` provider billing settlement and invoice reconciliation
|
||||
packet is now landed in current code
|
||||
- the current next bounded move is a source-backed `Phase 6R-AE` first-party provider settlement
|
||||
exception and external-portal handoff preparation/control pass, while keeping actual payment
|
||||
execution, provider-portal ownership, real device-permission workflow, native capture-route
|
||||
ownership, and actual payload shipping separately deferred
|
||||
- the repo-row implementation queue is now live from that `Phase 6R-A` entry point rather than
|
||||
waiting on another first-party packet
|
||||
|
||||
|
|
@ -269,16 +273,16 @@ Current routing truth:
|
|||
- `brianpeiris/RiftSketch`
|
||||
- the v6.3 CSV label `not_live_reference_or_discard_candidate` is not the final state by itself; read the closed `0R-E` packet for the retained-versus-discarded split
|
||||
|
||||
The next bounded move is a source-backed `Phase 6R-AD` first-party provider
|
||||
billing settlement and invoice reconciliation preparation/control pass.
|
||||
The next bounded move is a source-backed `Phase 6R-AE` first-party provider
|
||||
settlement exception and external-portal handoff preparation/control pass.
|
||||
|
||||
Queue interpretation after that control pass:
|
||||
|
||||
- then continue with the retained repo-row queue
|
||||
- highest current repo-row queue pressure sits in:
|
||||
- the first-party provider billing settlement and invoice reconciliation
|
||||
remainder above
|
||||
the partially landed `whisper.cpp` and `faster-whisper` speech seams
|
||||
- the first-party provider settlement exception and external-portal handoff
|
||||
remainder above the partially landed `whisper.cpp` and `faster-whisper`
|
||||
speech seams
|
||||
- `HactarCE/Hyperspeedcube` is now closed for the currently justified retained row:
|
||||
- landed:
|
||||
- puzzle catalog contract
|
||||
|
|
@ -371,6 +375,8 @@ Queue interpretation after that control pass:
|
|||
- first-party provider usage/cost history/export shell boundary
|
||||
- first-party provider receipt review and posted-charge inspection shell
|
||||
boundary
|
||||
- first-party provider billing settlement and invoice reconciliation shell
|
||||
boundary
|
||||
- bounded shell capability exposure for:
|
||||
- microphone capture shell
|
||||
- device-permission shell
|
||||
|
|
@ -391,8 +397,11 @@ Queue interpretation after that control pass:
|
|||
- provider receipt review shell
|
||||
- provider posted-charge inspection
|
||||
- provider charge-variance review
|
||||
- provider billing settlement shell
|
||||
- provider invoice reconciliation
|
||||
- provider settlement-readiness review
|
||||
- still deferred:
|
||||
- provider billing settlement and invoice reconciliation shell surfaces
|
||||
- provider settlement exception and external-portal handoff shell surfaces
|
||||
- actual payment execution or provider-portal ownership
|
||||
- real device-permission workflow
|
||||
- native capture-route ownership beyond bounded shell posture
|
||||
|
|
@ -441,12 +450,12 @@ Queue interpretation after that control pass:
|
|||
- viseme / gesture runtime integration
|
||||
- broad assistant-platform scope
|
||||
- the next queue shape is now:
|
||||
- first-party provider billing settlement and invoice reconciliation shell
|
||||
- first-party provider settlement exception and external-portal handoff
|
||||
assessment above the landed speech session, shell, payload-custody,
|
||||
provider-custody, provider-routing, normalized usage/cost, operator-facing
|
||||
dashboard, history/export, and receipt-review seams
|
||||
dashboard, history/export, receipt-review, and settlement seams
|
||||
- the next bounded move should stay narrow:
|
||||
- a source-backed `Phase 6R-AD` control pass before any widening into actual
|
||||
- a source-backed `Phase 6R-AE` control pass before any widening into actual
|
||||
payment execution, provider-portal ownership, actual payload shipping,
|
||||
real device-permission workflow, broad voice-output ownership, or broad
|
||||
assistant-platform scope
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue