Documentation
¶
Index ¶
- Constants
- func ConceptIDs(cs []*Concept) []string
- func DottedPlay(playString string) string
- func ToolSchemas(tools []interfaces.Tool) string
- type Analyzer
- func (a *Analyzer) BuildFacts(sim *montecarlo.Simmer, ss *stats.SimStats, req *ComparisonRequest, ...) (*PositionFacts, error)
- func (a *Analyzer) EvaluateLeave(leave string) (float64, error)
- func (a *Analyzer) Facts() *PositionFacts
- func (a *Analyzer) GetFuturePlayMetadata(playString string) (*FuturePlayMetadata, error)
- func (a *Analyzer) GetPlayMetadata(playString string) (*PlayMetadata, error)
- func (a *Analyzer) LookupFuturePlay(playString string) (*FuturePlayLookup, error)
- func (a *Analyzer) SetConfig(cfg *config.Config)
- func (a *Analyzer) SetGame(tp *bot.BotTurnPlayer)
- type Comparison
- type ComparisonRequest
- type Concept
- type Config
- type Deltas
- type EvaluateLeaveTool
- func (t *EvaluateLeaveTool) Description() string
- func (t *EvaluateLeaveTool) Execute(ctx context.Context, args string) (string, error)
- func (t *EvaluateLeaveTool) Name() string
- func (t *EvaluateLeaveTool) Parameters() map[string]interfaces.ParameterSpec
- func (t *EvaluateLeaveTool) Run(ctx context.Context, args string) (string, error)
- type Exchange
- type ExplainInput
- type ExplainResult
- type Flags
- type FollowupCluster
- type FollowupFact
- type FuturePlayFamily
- type FuturePlayLookup
- type FuturePlayMetadata
- type GetOurFuturePlayMetadataTool
- func (t *GetOurFuturePlayMetadataTool) Description() string
- func (t *GetOurFuturePlayMetadataTool) Execute(ctx context.Context, args string) (string, error)
- func (t *GetOurFuturePlayMetadataTool) Name() string
- func (t *GetOurFuturePlayMetadataTool) Parameters() map[string]interfaces.ParameterSpec
- func (t *GetOurFuturePlayMetadataTool) Run(ctx context.Context, args string) (string, error)
- type GetOurPlayMetadataTool
- func (t *GetOurPlayMetadataTool) Description() string
- func (t *GetOurPlayMetadataTool) Execute(ctx context.Context, args string) (string, error)
- func (t *GetOurPlayMetadataTool) Name() string
- func (t *GetOurPlayMetadataTool) Parameters() map[string]interfaces.ParameterSpec
- func (t *GetOurPlayMetadataTool) Run(ctx context.Context, args string) (string, error)
- type InferenceFacts
- type InferenceInput
- type LaneComparison
- type Phase
- type PlayMetadata
- type PlayNotFoundError
- type PositionFacts
- type Prompt
- type Service
Constants ¶
const InferredRacksShown = 5
InferredRacksShown is how many of the most likely leaves belong in a prompt. The tail is a long list of near-identical racks and says nothing the top few don't. Exported because the caller is what asks the rangefinder for them.
const (
// ResponseBanner introduces what the model sent back.
ResponseBanner = bannerRule + " MODEL RESPONSE " + bannerRule
)
Banners dividing a dump into sections. Each one labels everything below it up to the next banner, which is the only workable rule: the prompt is markdown, so a per-line marker can't distinguish our commentary from the content - "## What to say" is a heading in system.md and really is sent.
The eight-equals form is deliberate. A markdown setext heading is a line of nothing but "=", so a banner with text in it can't be mistaken for one in the model's reply.
Variables ¶
This section is empty.
Functions ¶
func ConceptIDs ¶ added in v0.13.6
ConceptIDs names the cards, for tests and for logging which ones a position pulled in.
func DottedPlay ¶ added in v0.13.6
DottedPlay converts a play from the playthrough notation the tables use, 5D (S)PIC(A), into the dotted form the move parser wants, 5D .PIC. - the parenthesized tiles are the ones already on the board. Exchanges and passes come back unchanged apart from their wrapping parentheses. Input that is already dotted is left alone.
func ToolSchemas ¶ added in v0.13.6
func ToolSchemas(tools []interfaces.Tool) string
ToolSchemas renders the tool definitions as JSON Schema, the way the OpenAI-compatible providers serialize them. Gemini builds an equivalent genai.Schema out of the same three pieces - name, description, parameters - so this is the shape of what every provider sends, not the exact bytes of any one of them.
Types ¶
type Analyzer ¶
type Analyzer struct {
// contains filtered or unexported fields
}
Analyzer holds the game and the facts we computed about the position, and answers the tools' questions from them. Nothing here parses the prompt.
func NewAnalyzer ¶
func NewAnalyzer() *Analyzer
func (*Analyzer) BuildFacts ¶ added in v0.13.6
func (a *Analyzer) BuildFacts(sim *montecarlo.Simmer, ss *stats.SimStats, req *ComparisonRequest, inf *InferenceInput) (*PositionFacts, error)
BuildFacts assembles the fact pack for the current position from a finished simulation, and keeps it as what the tools answer from. It must be called after the sim has stopped. A non-nil req asks for a head-to-head against that play; the simulation must already have evaluated it, which is what Simmer.AvoidPruningMoves is for. A non-nil inf adds what a read on the opponent's rack changed, if anything.
func (*Analyzer) EvaluateLeave ¶
EvaluateLeave evaluates the value of a leave
func (*Analyzer) Facts ¶ added in v0.13.6
func (a *Analyzer) Facts() *PositionFacts
Facts returns the fact pack built for the current position, if any.
func (*Analyzer) GetFuturePlayMetadata ¶
func (a *Analyzer) GetFuturePlayMetadata(playString string) (*FuturePlayMetadata, error)
GetFuturePlayMetadata analyzes a potential future play. If the play can be made in more than one way it returns the most likely one; use LookupFuturePlay to see every way.
func (*Analyzer) GetPlayMetadata ¶
func (a *Analyzer) GetPlayMetadata(playString string) (*PlayMetadata, error)
GetPlayMetadata analyzes a play and returns metadata
func (*Analyzer) LookupFuturePlay ¶ added in v0.13.6
func (a *Analyzer) LookupFuturePlay(playString string) (*FuturePlayLookup, error)
LookupFuturePlay looks a follow-up play up in the facts computed for this position. It returns a *PlayNotFoundError if the play isn't one the simulation sampled.
func (*Analyzer) SetGame ¶ added in v0.13.6
func (a *Analyzer) SetGame(tp *bot.BotTurnPlayer)
SetGame sets the position under analysis.
type Comparison ¶ added in v0.13.6
type Comparison struct {
// Play is what we were asked to contrast the best play with.
Play string `json:"play"`
// FromHistory is true when it is the move the player actually made, as
// opposed to one they named.
FromHistory bool `json:"from_history"`
// WasBest is true when the play we were asked about *is* the top play. It
// then has nothing to be contrasted with, so Rival below is the runner-up
// instead and there is still something to learn.
WasBest bool `json:"was_best"`
// Rival is the candidate actually contrasted against.
Rival *montecarlo.CandidateStats `json:"rival"`
RivalPlayStats *stats.PlayStats `json:"-"`
RivalFollowups []*FollowupFact `json:"rival_followups"`
// RivalChances is RivalFollowups gathered into opportunities.
RivalChances []*FollowupCluster `json:"rival_chances"`
// TypicalNextScore is what an ordinary next turn is worth after the rival.
TypicalNextScore float64 `json:"typical_next_score"`
Deltas Deltas `json:"deltas"`
// OnlyBest and OnlyRival are the worthwhile follow-up plays each side has
// that the other doesn't - the concrete "what you gave up" list.
OnlyBest []*FollowupFact `json:"only_best"`
OnlyRival []*FollowupFact `json:"only_rival"`
}
Comparison answers "why is this better than the move I made?". The play in question is normally the one the player actually made, taken from the game history; it can also be named outright.
type ComparisonRequest ¶ added in v0.13.6
type ComparisonRequest struct {
Move *move.Move
// FromHistory distinguishes "the move you actually made", which we found
// ourselves, from one the user named.
FromHistory bool
}
ComparisonRequest names a play the best one should be contrasted with.
type Concept ¶ added in v0.13.6
type Concept struct {
// ID is the card's name, taken from its frontmatter.
ID string
// Priority orders the cards in the prompt, lowest first.
Priority int
// When lists flag names. The card ships if any of them is set, so
// "always" is just a flag that is always true.
When []string
Body string
}
Concept is one card: a piece of Scrabble knowledge plus the conditions under which it is worth spending prompt on.
func SelectConcepts ¶ added in v0.13.6
SelectConcepts returns the cards this position calls for, in prompt order.
type Config ¶
type Config struct {
Provider string // "gemini", "openai", "openrouter", or "deepseek"
APIKey string
Model string
BaseURL string // optional: override base URL (e.g. for LM Studio at http://127.0.0.1:1234/v1)
UseQuirky bool
}
Config holds configuration for the explainer service
func DefaultConfig ¶
DefaultConfig returns a default configuration from macondo config
type Deltas ¶ added in v0.13.6
type Deltas struct {
WinPct float64 `json:"win_pct"`
Equity float64 `json:"equity"`
Score int `json:"score"`
LeaveValue float64 `json:"leave_value"`
// The opponent's reply, one ply out: a negative OppMeanScore means the
// best play holds them to less.
OppMeanScore float64 `json:"opp_mean_score"`
OppStdev float64 `json:"opp_stdev"`
OppBingoPct float64 `json:"opp_bingo_pct"`
// Our own next turn, two plies out.
OurMeanScore float64 `json:"our_mean_score"`
OurBingoPct float64 `json:"our_bingo_pct"`
// BestUpside and RivalUpside are the expected points each play's big
// follow-up chances are worth on top of an ordinary turn. The gap between
// them is often the whole reason one play wins, and it is invisible in
// every other figure here.
BestUpside float64 `json:"best_upside"`
RivalUpside float64 `json:"rival_upside"`
// Established is true when the two win% confidence intervals don't
// overlap. When it is false the simulation has not actually shown one
// play to be better, and saying so would be overclaiming.
Established bool `json:"established"`
}
Deltas is the head-to-head arithmetic between the best play and the one it is being contrasted with. Every figure is best minus rival, so a positive number is the best play's advantage.
type EvaluateLeaveTool ¶
type EvaluateLeaveTool struct {
// contains filtered or unexported fields
}
EvaluateLeaveTool evaluates the value of a leave
func NewEvaluateLeaveTool ¶
func NewEvaluateLeaveTool(analyzer *Analyzer) *EvaluateLeaveTool
func (*EvaluateLeaveTool) Description ¶
func (t *EvaluateLeaveTool) Description() string
func (*EvaluateLeaveTool) Name ¶
func (t *EvaluateLeaveTool) Name() string
func (*EvaluateLeaveTool) Parameters ¶
func (t *EvaluateLeaveTool) Parameters() map[string]interfaces.ParameterSpec
type Exchange ¶ added in v0.13.6
type Exchange struct {
Prompt *Prompt
Response string
// BestPlay and Comparison say what the explanation was about, so a dump
// after the fact is identifiable.
BestPlay string
Comparison string
}
Exchange is what was last sent to the model and what came back. The prompt is assembled from so many computed pieces that being able to read the exact thing that produced an explanation is the only practical way to debug one.
type ExplainInput ¶ added in v0.13.6
type ExplainInput struct {
Game *bot.BotTurnPlayer
Simmer *montecarlo.Simmer
SimStats *stats.SimStats
// Compare asks for a head-to-head against a particular play, usually the
// one the player actually made. The simulation must already have
// evaluated it - see Simmer.AvoidPruningMoves.
Compare *ComparisonRequest
// Inference is the read on the opponent's rack that Simmer was run with,
// plus the same plays simmed without it. Nil when no read was taken.
Inference *InferenceInput
// Model overrides the configured model for this one explanation, so that
// several can be tried on the same position without editing any config.
// Empty means whatever the provider is configured to use.
Model string
}
ExplainInput is the position to explain. The service builds everything it sends to the model from these three, so nobody has to pre-render tables and nobody has to parse them back.
type ExplainResult ¶
type ExplainResult struct {
Explanation string
InputTokens int
OutputTokens int
// Provider and Model say who actually answered. Worth reporting because
// both can now change between one explanation and the next.
Provider string
Model string
// Concepts names the concept cards this position pulled into the prompt.
Concepts []string
// Prompt is exactly what was sent. Also kept on the service, so it can be
// read back after the fact.
Prompt *Prompt
}
ExplainResult contains the explanation from the AI
type Flags ¶ added in v0.13.6
Flags are the yes/no answers that decide which concept cards get sent. Every one of them replaces a paragraph that used to ship on every single call.
type FollowupCluster ¶ added in v0.13.6
type FollowupCluster struct {
// Anchor is where the opportunity is: the tiles it runs through and the
// lane they are in, like "(QUAD) in row 2", or a bare square when there is
// nothing on the board to name it by.
Anchor string `json:"anchor"`
// Through is the letters already on the board that every play here runs
// through, empty when the opportunity is an open square rather than a hook.
Through string `json:"through"`
// Plays are the ways of taking it, most likely first.
Plays []*FollowupFact `json:"plays"`
// Pct, MinScore and MaxScore cover the cluster as a whole.
Pct float64 `json:"pct"`
AvgScore float64 `json:"avg_score"`
MinScore int `json:"min_score"`
MaxScore int `json:"max_score"`
IsSetup bool `json:"is_setup"`
IsBigChance bool `json:"is_big_chance"`
Upside float64 `json:"upside"`
}
FollowupCluster is one opportunity, and every play that takes it. Plays that start on the same square and run through the same tiles already on the board are the same chance reached by different draws - 2F (QUAD)RUPLE, RUPLY, RUPED and RUPOLE are one hook and four ways of filling it.
The distinction is the whole reason this type exists. Judged one at a time those four are 4.91%, 4.81%, 3.49% and 2.58% - each individually too rare to build a turn around, and each rejected. Together they are 15.79% of the time for an average of 48.6 points, which is the single biggest thing about the position. A chance split across spellings is still one chance.
func (*FollowupCluster) Grouped ¶ added in v0.13.6
func (c *FollowupCluster) Grouped() bool
Grouped reports whether this is several plays rather than one.
func (*FollowupCluster) Label ¶ added in v0.13.6
func (c *FollowupCluster) Label() string
Label names the cluster. One play is named by itself. Several sharing a hook are named by the hook - "(QUAD) in row 2" says what the chance is. Several sharing only a square have no hook to name, and a bare "J9" says nothing at all, so those are named by their words: "J9 CAPERED / PEASCOD".
func (*FollowupCluster) NeededDraws ¶ added in v0.13.6
func (c *FollowupCluster) NeededDraws() []string
NeededDraws is every draw that reaches any play in the cluster, in the order the plays came in and without repeats.
func (*FollowupCluster) Requirement ¶ added in v0.13.6
func (c *FollowupCluster) Requirement() string
Requirement is what has to happen before the cluster's likeliest play is available.
func (*FollowupCluster) Worthwhile ¶ added in v0.13.6
func (c *FollowupCluster) Worthwhile() bool
Worthwhile reports whether the cluster is worth a sentence.
type FollowupFact ¶ added in v0.13.6
type FollowupFact struct {
*stats.FollowupFamily
// WayRequirements runs parallel to Ways. Each entry is "none", "requires
// opponent play", or "requires us to play <best play> first". The last is
// what actually makes a setup a setup: the follow-up doesn't exist unless
// we make this play.
WayRequirements []string `json:"way_requirements"`
// IsSetup means our best play creates this opportunity.
IsSetup bool `json:"is_setup"`
// IsBigChance means the play is far bigger than an ordinary next turn and
// comes up often enough for that to be worth playing for, whether or not
// our play is what created it.
IsBigChance bool `json:"is_big_chance"`
// Upside is expected points above an ordinary next turn: how often this
// play comes up times how much bigger than average it is.
Upside float64 `json:"upside"`
}
FollowupFact is one of our sampled follow-up plays, with the judgments the model used to have to make about it already made.
func (*FollowupFact) AvgScore ¶ added in v0.13.6
func (f *FollowupFact) AvgScore() float64
AvgScore is the score across the ways of making this play, weighted by how often each came up. A grouped play's MaxScore is only its luckiest route, so judging the play by it would overstate what it is worth.
func (*FollowupFact) Requirement ¶ added in v0.13.6
func (f *FollowupFact) Requirement() string
Requirement is what has to happen before the play's most likely route is available.
func (*FollowupFact) Worthwhile ¶ added in v0.13.6
func (f *FollowupFact) Worthwhile() bool
Worthwhile reports whether the follow-up is worth a sentence: either our play creates it, or it is big enough and likely enough to matter.
type FuturePlayFamily ¶ added in v0.13.6
type FuturePlayFamily struct {
Play string `json:"play"`
CombinedPercent float64 `json:"combined_probability_percent"`
ScoreRange string `json:"score_range"`
NeededDrawOptions []string `json:"needed_draw_options"` // any one of these unlocks the play
IsSetup bool `json:"is_setup"`
}
FuturePlayFamily describes a play that can be made in more than one way, because a blank can stand in for different tiles. The combined percentage is the one to quote: each individual way is only part of the chance of making the play.
type FuturePlayLookup ¶ added in v0.13.6
type FuturePlayLookup struct {
Asked string // the specific way the model named, if it named one
Family *FuturePlayFamily
Ways []*FuturePlayMetadata
}
FuturePlayLookup is the result of looking a follow-up play up in the sim stats: every way of making it, plus the combined figures when there is more than one.
type FuturePlayMetadata ¶
type FuturePlayMetadata struct {
Play string `json:"play"`
Score int `json:"score"`
IsBingo bool `json:"is_bingo"`
NeededDraw []string `json:"needed_draw"` // tiles needed from bag
RequiresOtherPlay string `json:"requires_opp_play"` // opponent play needed first
ProbabilityPercent float64 `json:"probability_percent"` // likelihood of this play
IsSetup bool `json:"is_setup"`
}
FuturePlayMetadata represents metadata about a potential future play
type GetOurFuturePlayMetadataTool ¶
type GetOurFuturePlayMetadataTool struct {
// contains filtered or unexported fields
}
GetOurFuturePlayMetadataTool analyzes metadata for a potential future play
func NewGetOurFuturePlayMetadataTool ¶
func NewGetOurFuturePlayMetadataTool(analyzer *Analyzer) *GetOurFuturePlayMetadataTool
func (*GetOurFuturePlayMetadataTool) Description ¶
func (t *GetOurFuturePlayMetadataTool) Description() string
func (*GetOurFuturePlayMetadataTool) Name ¶
func (t *GetOurFuturePlayMetadataTool) Name() string
func (*GetOurFuturePlayMetadataTool) Parameters ¶
func (t *GetOurFuturePlayMetadataTool) Parameters() map[string]interfaces.ParameterSpec
type GetOurPlayMetadataTool ¶
type GetOurPlayMetadataTool struct {
// contains filtered or unexported fields
}
GetOurPlayMetadataTool analyzes metadata for a current play
func NewGetOurPlayMetadataTool ¶
func NewGetOurPlayMetadataTool(analyzer *Analyzer) *GetOurPlayMetadataTool
func (*GetOurPlayMetadataTool) Description ¶
func (t *GetOurPlayMetadataTool) Description() string
func (*GetOurPlayMetadataTool) Name ¶
func (t *GetOurPlayMetadataTool) Name() string
func (*GetOurPlayMetadataTool) Parameters ¶
func (t *GetOurPlayMetadataTool) Parameters() map[string]interfaces.ParameterSpec
type InferenceFacts ¶ added in v0.13.6
type InferenceFacts struct {
Summary *rangefinder.InferenceSummary `json:"summary"`
// Outliers are the tiles the read actually says something about, ordered
// by how far it moved them.
Outliers []rangefinder.TileDeviation `json:"outliers"`
// ShapeRead is set when the read is about the makeup of the rack rather
// than about any one tile. It is a separate finding because it can be the
// only one: a read can leave every letter within a tenth of a tile of
// chance and still say, unmistakably, that they kept consonants.
ShapeRead *rangefinder.RackShape `json:"shape_read,omitempty"`
// Baseline is the same plays simmed without the read, so we can say what
// the read changed rather than only what it concluded.
Baseline []montecarlo.CandidateStats `json:"-"`
// BaselineBest is the play we would have recommended knowing nothing.
BaselineBest *montecarlo.CandidateStats `json:"baseline_best"`
// BaselineOfBest is the recommended play's own showing in that sim, which
// is what WinPctShift is measured against. Nil if it wasn't simmed there.
BaselineOfBest *montecarlo.CandidateStats `json:"baseline_of_best"`
// ChangedTopPlay is the strongest thing a read can do: recommend a
// different play than we would have made without it.
ChangedTopPlay bool `json:"changed_top_play"`
// WinPctShift is the recommended play's win% with the read minus without.
WinPctShift float64 `json:"win_pct_shift"`
// Established is true when that shift is outside both sims' confidence
// intervals. The two runs stop independently, so they aren't equally
// sampled - non-overlapping intervals still means a real difference, but
// the size of it shouldn't be read too precisely.
Established bool `json:"established"`
// Decisive is Established plus a practical significance test. A shift can
// be statistically real and mean nothing: in a position already won, win
// probabilities saturate, their intervals shrink to almost nothing, and a
// swing from 97.9% to 97.0% clears the statistical bar while changing
// nothing about how to play. Decisive is what gates the subject.
Decisive bool `json:"decisive"`
// Informative is true when the posterior deviates from chance enough to
// be worth a sentence.
Informative bool `json:"informative"`
// Matters gates the whole subject. An uninformative read, or one that
// moves nothing, is not mentioned at all.
Matters bool `json:"matters"`
}
InferenceFacts is what the opponent's last play gave away, and whether it changed anything. Both halves matter: a read that says something startling about their rack but leaves the recommendation untouched is a curiosity, not a lesson, and neither half is worth prompt on its own.
type InferenceInput ¶ added in v0.13.6
type InferenceInput struct {
Summary *rangefinder.InferenceSummary
// Baseline is CandidateStats from a sim run with inference off, best
// win% first.
Baseline []montecarlo.CandidateStats
}
InferenceInput is a read on the opponent's rack, together with the same plays simmed without it. Both are needed: the read on its own can't say what it changed.
type LaneComparison ¶ added in v0.13.6
LaneComparison is where one candidate's sampled opponent replies land.
type Phase ¶ added in v0.13.6
type Phase string
Phase is where in the game we are. It decides which advice applies.
const ( PhaseOpening Phase = "opening" PhaseMidgame Phase = "midgame" // PhaseLateMidgame is the stretch where the endgame is coming but the // position is not yet a pre-endgame. Naming it keeps the model from // reaching for pre-endgame language too early. PhaseLateMidgame Phase = "late midgame (endgame approaching)" PhasePreEndgame Phase = "pre-endgame" )
type PlayMetadata ¶
type PlayMetadata struct {
Play string `json:"play"`
Score int `json:"score"`
TilesUsed int `json:"tiles_used"`
IsBingo bool `json:"is_bingo"`
VowelsInLeave int `json:"vowels_in_leave"`
ConsonantsLeft int `json:"consonants_in_leave"`
LeaveBalance string `json:"leave_balance"` // "balanced", "vowel-heavy", "consonant-heavy"
}
PlayMetadata represents metadata about a Scrabble play
type PlayNotFoundError ¶ added in v0.13.6
PlayNotFoundError is returned when a play is not one of the sampled follow-up plays. It carries the plays we do know about so that the tool can hand them back to the model instead of letting it guess at spellings.
func (*PlayNotFoundError) Error ¶ added in v0.13.6
func (e *PlayNotFoundError) Error() string
func (*PlayNotFoundError) ToolMessage ¶ added in v0.13.6
func (e *PlayNotFoundError) ToolMessage() string
ToolMessage is what we hand back to the LLM. It is deliberately not an error; a plain result that lists the valid plays lets the model correct itself in a single turn instead of retrying the same lookup.
type PositionFacts ¶ added in v0.13.6
type PositionFacts struct {
Rack string
Lexicon string
BagCount int
UnseenCount int
OppRackSize int
Spread int
Phase Phase
UnseenVowels int
UnseenConsonants int
UnseenBlanks int
UnseenPowerTiles []string
Iterations int
Candidates []montecarlo.CandidateStats
Best *montecarlo.CandidateStats
PlayStats *stats.PlayStats
Followups []*FollowupFact
// Chances is Followups gathered into opportunities: the plays that take the
// same hook are one chance, and are judged and reported as one. Ordered by
// upside, biggest first.
Chances []*FollowupCluster
Lanes []*LaneComparison
// TypicalNextScore is what an ordinary next turn is worth after the best
// play. It is the yardstick every follow-up chance is measured against.
TypicalNextScore float64
// BestByEquity is the candidate with the highest equity, which is not
// always the one with the highest win%.
BestByEquity *montecarlo.CandidateStats
// Comparison is set when we were asked why the best play beats some other
// play - usually the one the player actually made.
Comparison *Comparison
// Inference is set when the position was analyzed with a read on the
// opponent's rack. It is nil unless `explain -infer` asked for one.
Inference *InferenceFacts
Flags Flags
}
PositionFacts is everything we know about the position, computed rather than described.
func (*PositionFacts) Ask ¶ added in v0.13.6
func (f *PositionFacts) Ask() string
Ask is the question the explanation has to answer, which is a different question when the reader has a play of their own on the table.
func (*PositionFacts) Render ¶ added in v0.13.6
func (f *PositionFacts) Render() string
Render writes the position, the candidates, and what the simulation saw happen next.
func (*PositionFacts) SetFollowups ¶ added in v0.13.6
func (f *PositionFacts) SetFollowups(fs []*FollowupFact)
SetFollowups records the sampled follow-ups and gathers them into the opportunities they take. The two are always set together - the flat list is the table, the clusters are what gets judged and reported - and going through here is what keeps them from disagreeing.
Best and TypicalNextScore have to be set first: a chance is only big or small relative to what an ordinary turn after this play is worth.
type Prompt ¶ added in v0.13.6
type Prompt struct {
System string
User string
// Concepts names the cards this position pulled in. Useful for logging
// and for tests that assert a position asks for the right knowledge.
Concepts []string
// Tools are the function definitions sent alongside the messages. They
// appear in neither message's text - the API carries them in a field of
// their own - so a dump showing only the messages would be missing
// instructions the model really does receive, and some of them are
// substantive: get_our_future_play_metadata's description is what tells
// the model it may only look up plays from the follow-up table.
Tools []interfaces.Tool
}
Prompt is what gets sent: stable instructions in the system message, and everything that changes with the position in the user message. Splitting them this way is also what lets a provider cache the system half.
func BuildPrompt ¶ added in v0.13.6
func BuildPrompt(f *PositionFacts, quirky bool) (*Prompt, error)
BuildPrompt assembles the prompt for a position. Which concept cards it includes is decided by the facts, not by the model and not by a human guessing which ones might come up.
func (*Prompt) Notes ¶ added in v0.13.6
Notes is Macondo's own commentary to print above a prompt dump: what the prompt was for, and which concept cards the position pulled in. It ends with a banner, so everything it says is bounded by position rather than by any per-line marker. headline, if given, says what the prompt was about.
type Service ¶
type Service struct {
// contains filtered or unexported fields
}
Service provides the main explainer service
func NewService ¶
NewService creates a new explainer service
func (*Service) Explain ¶
func (s *Service) Explain(ctx context.Context, in *ExplainInput) (*ExplainResult, error)
Explain generates an explanation for a finished simulation.
func (*Service) LastExchange ¶ added in v0.13.6
LastExchange returns the most recent prompt and response, or nil if this service hasn't explained anything yet.