237 lines
7.0 KiB
Go
237 lines
7.0 KiB
Go
package insight
|
|
|
|
import (
|
|
"encoding/json"
|
|
"errors"
|
|
"time"
|
|
)
|
|
|
|
type Module string
|
|
|
|
type Section string
|
|
|
|
const (
|
|
ModuleOverview Module = "overview"
|
|
ModulePayments Module = "payments"
|
|
ModuleFees Module = "fees"
|
|
ModulePeers Module = "peers"
|
|
ModuleTransactions Module = "transactions"
|
|
ModuleGrowth Module = "growth"
|
|
)
|
|
|
|
const (
|
|
SectionFull Section = "full"
|
|
SectionPast Section = "past"
|
|
SectionLatestObserved Section = "latest-observed"
|
|
SectionFuture7Days Section = "future-7-days"
|
|
SectionFuture30Days Section = "future-30-days"
|
|
SectionFuture90Days Section = "future-90-days"
|
|
SectionFindings Section = "findings"
|
|
SectionOptions Section = "options"
|
|
)
|
|
|
|
var ErrNotFound = errors.New("ai insight: not found")
|
|
var ErrUnknownTerminal = errors.New("ai insight: unknown terminal")
|
|
var ErrRefreshLimited = errors.New("ai insight: refresh limited")
|
|
var ErrRefreshCooldown = errors.New("ai insight: refresh cooldown")
|
|
var ErrIdempotencyConflict = errors.New("ai insight: idempotency conflict")
|
|
var ErrOnboardingRequired = errors.New("ai insight: onboarding required")
|
|
|
|
type RefreshCooldownError struct{ NextRefreshAt time.Time }
|
|
|
|
func (e *RefreshCooldownError) Error() string { return "ai insight: refresh cooldown" }
|
|
func (e *RefreshCooldownError) Unwrap() error { return ErrRefreshCooldown }
|
|
|
|
func ParseModule(value string) (Module, bool) {
|
|
module := Module(value)
|
|
switch module {
|
|
case ModuleOverview, ModulePayments, ModuleFees, ModulePeers, ModuleTransactions, ModuleGrowth:
|
|
return module, true
|
|
default:
|
|
return "", false
|
|
}
|
|
}
|
|
|
|
func ParseSection(module Module, value string) (Section, bool) {
|
|
section := Section(value)
|
|
switch section {
|
|
case SectionPast, SectionLatestObserved, SectionFuture7Days, SectionFuture30Days, SectionFindings, SectionOptions:
|
|
return section, true
|
|
case SectionFuture90Days:
|
|
return section, module == ModuleGrowth
|
|
default:
|
|
return "", false
|
|
}
|
|
}
|
|
|
|
type Confidence string
|
|
|
|
const (
|
|
ConfidenceHigh Confidence = "HIGH"
|
|
ConfidenceMedium Confidence = "MEDIUM"
|
|
ConfidenceLow Confidence = "LOW"
|
|
ConfidenceInsufficientData Confidence = "INSUFFICIENT_DATA"
|
|
)
|
|
|
|
type Narrative struct {
|
|
Headline string `json:"headline"`
|
|
Body string `json:"body"`
|
|
EvidenceID []string `json:"evidenceIds"`
|
|
}
|
|
|
|
type Finding struct {
|
|
EvidenceID string `json:"evidenceId"`
|
|
Title string `json:"title"`
|
|
Summary string `json:"summary"`
|
|
ScoreBPS int `json:"scoreBps"`
|
|
Confidence Confidence `json:"confidence"`
|
|
}
|
|
|
|
type Option struct {
|
|
Title string `json:"title"`
|
|
Description string `json:"description"`
|
|
EvidenceID []string `json:"evidenceIds"`
|
|
}
|
|
|
|
type CalendarContext struct {
|
|
EventKey string `json:"eventKey"`
|
|
TitleFA string `json:"titleFa"`
|
|
StartDate string `json:"startDate"`
|
|
EndDate string `json:"endDate"`
|
|
Tone string `json:"tone"`
|
|
CommercialPosture string `json:"commercialPosture"`
|
|
OfficialHoliday bool `json:"officialHoliday"`
|
|
EvidenceSupported bool `json:"evidenceSupported"`
|
|
OverlapConfounded bool `json:"overlapConfounded"`
|
|
}
|
|
|
|
type Copy struct {
|
|
Past Narrative `json:"past"`
|
|
LatestObserved Narrative `json:"latestObserved"`
|
|
Future7Days Narrative `json:"future7Days"`
|
|
Future30Days Narrative `json:"future30Days"`
|
|
Future90Days *Narrative `json:"future90Days,omitempty"`
|
|
Findings []Finding `json:"findings"`
|
|
Options []Option `json:"options"`
|
|
}
|
|
|
|
func MergeSection(base, generated Copy, section Section) (Copy, error) {
|
|
switch section {
|
|
case SectionFull:
|
|
return generated, nil
|
|
case SectionPast:
|
|
base.Past = generated.Past
|
|
case SectionLatestObserved:
|
|
base.LatestObserved = generated.LatestObserved
|
|
case SectionFuture7Days:
|
|
base.Future7Days = generated.Future7Days
|
|
case SectionFuture30Days:
|
|
base.Future30Days = generated.Future30Days
|
|
case SectionFuture90Days:
|
|
if generated.Future90Days == nil {
|
|
return Copy{}, errors.New("ai insight: future 90 day section unavailable")
|
|
}
|
|
base.Future90Days = generated.Future90Days
|
|
case SectionFindings:
|
|
base.Findings = generated.Findings
|
|
case SectionOptions:
|
|
base.Options = generated.Options
|
|
default:
|
|
return Copy{}, errors.New("ai insight: invalid section")
|
|
}
|
|
return base, nil
|
|
}
|
|
|
|
type Scope struct {
|
|
TerminalID *string `json:"terminalId"`
|
|
PeriodID *string `json:"periodId,omitempty"`
|
|
}
|
|
|
|
type PeriodScope struct {
|
|
PeriodID string
|
|
PeriodType string
|
|
StartAt time.Time
|
|
EndAt time.Time
|
|
JalaliLabelFA string
|
|
}
|
|
|
|
type RefreshState struct {
|
|
State string `json:"state"`
|
|
JobID string `json:"jobId,omitempty"`
|
|
EventsURL string `json:"eventsUrl,omitempty"`
|
|
NextRefreshAt *time.Time `json:"nextRefreshAt,omitempty"`
|
|
}
|
|
|
|
type Snapshot struct {
|
|
InsightID string `json:"insightId"`
|
|
Module Module `json:"module"`
|
|
Revision int `json:"revision"`
|
|
Status string `json:"status"`
|
|
Scope Scope `json:"scope"`
|
|
AsOfDate string `json:"asOfDate"`
|
|
DataThroughDate string `json:"dataThroughDate"`
|
|
ForecastStart string `json:"forecastStartDate"`
|
|
Freshness Freshness `json:"freshness"`
|
|
DataLagDays int `json:"dataLagDays"`
|
|
Confidence Confidence `json:"confidence"`
|
|
Copy Copy `json:"copy"`
|
|
Evidence json.RawMessage `json:"evidence"`
|
|
CalendarContext []CalendarContext `json:"calendarContext"`
|
|
RefreshState RefreshState `json:"refreshState"`
|
|
SectionRefresh map[Section]RefreshState `json:"sectionRefreshState"`
|
|
GeneratedAt time.Time `json:"generatedAt"`
|
|
}
|
|
|
|
type Job struct {
|
|
JobID string `json:"jobId"`
|
|
SnapshotID string `json:"snapshotId"`
|
|
Module Module `json:"module"`
|
|
Section Section `json:"sectionId"`
|
|
State string `json:"state"`
|
|
EventsURL string `json:"eventsUrl"`
|
|
CreatedAt time.Time `json:"createdAt"`
|
|
UpdatedAt time.Time `json:"updatedAt"`
|
|
}
|
|
|
|
type Event struct {
|
|
ID int64 `json:"eventId"`
|
|
Type string `json:"type"`
|
|
Payload json.RawMessage `json:"payload"`
|
|
OccurredAt time.Time `json:"occurredAt"`
|
|
}
|
|
|
|
type CreateRequest struct {
|
|
MerchantID int64
|
|
TerminalID string
|
|
Module Module
|
|
Section Section
|
|
TriggerReason string
|
|
ManualRefresh bool
|
|
IdempotencyKey string
|
|
RequestHash string
|
|
Fingerprint string
|
|
Time TimeContext
|
|
Confidence Confidence
|
|
CatalogVersion string
|
|
Evidence json.RawMessage
|
|
Period PeriodScope
|
|
}
|
|
|
|
type ProviderUsage struct {
|
|
InputTokens int64
|
|
OutputTokens int64
|
|
ProviderRequests int64
|
|
LatencyMS int64
|
|
ProviderRequestID string
|
|
EstimatedCost string
|
|
}
|
|
|
|
type Completion struct {
|
|
Analysis Analysis
|
|
AICopy *Copy
|
|
Status string
|
|
FailureCode string
|
|
Model string
|
|
Usage ProviderUsage
|
|
}
|