Source identity
| Field | Recorded value |
|---|---|
| Source type | GitHub source |
| Original record | Janady13/missionmatch / internal/modules/matching/skillmatcher.go |
| Date | Not specified in source |
| Repository | Janady13/missionmatch |
| Revision | 39df94f92b2a33f852481fe1954dabed841524bb |
| Branch | w0-aedt-compliance |
| Path or archive member | internal/modules/matching/skillmatcher.go |
| Original SHA-256 | 4348cda67e869c9c2f21c575070ce84b7fbda916484bb9400713133240db7711 |
| Git blob SHA | 752e9cdaa6ac38a605171f9ea1f8867154797a07 |
| Reading-copy SHA-256 | 4348cda67e869c9c2f21c575070ce84b7fbda916484bb9400713133240db7711 |
Recovered source transcript
Download complete source text ↓71 lines · 2.3 KB
package matching
import (
"context"
"github.com/shopspring/decimal"
"github.com/missionworkforce/missionmatch/internal/modules/matching/engine"
"github.com/missionworkforce/missionmatch/internal/platform/embeddings"
)
// EmbeddingSkillMatcher implements engine.SkillMatcher using the embedding provider: it embeds the
// required skill name and each candidate skill name and picks the best cosine match at/above the
// engine's threshold. Vectors from the provider are L2-normalized, so cosine == dot product.
//
// This is the semantic ("synonyms / adjacent skills") layer the engine defers to AFTER exact match.
// With the deterministic offline embedder, unrelated skills score near zero, so it never invents a
// false semantic match — exact matching still dominates. Determinism follows from the embedder's.
type EmbeddingSkillMatcher struct {
embedder embeddings.Embedder
ctx context.Context
}
// NewEmbeddingSkillMatcher wraps an embedder as an engine.SkillMatcher.
func NewEmbeddingSkillMatcher(e embeddings.Embedder) *EmbeddingSkillMatcher {
return &EmbeddingSkillMatcher{embedder: e, ctx: context.Background()}
}
// Match satisfies engine.SkillMatcher. It returns the candidate skill side and the similarity; the
// engine owns the required-side identity and the match flags.
func (m *EmbeddingSkillMatcher) Match(required engine.RequiredSkill, cands []engine.CandidateSkill, threshold decimal.Decimal) (engine.SkillMatch, bool) {
if m == nil || m.embedder == nil || len(cands) == 0 {
return engine.SkillMatch{}, false
}
rv, err := m.embedder.Embed(m.ctx, required.Name)
if err != nil {
return engine.SkillMatch{}, false
}
thr, _ := threshold.Float64()
bestSim := -2.0
var best engine.CandidateSkill
found := false
for _, cs := range cands {
cv, err := m.embedder.Embed(m.ctx, cs.Name)
if err != nil || len(cv) != len(rv) {
continue
}
if sim := dot(rv, cv); sim > bestSim {
bestSim, best, found = sim, cs, true
}
}
if !found || bestSim < thr {
return engine.SkillMatch{}, false
}
return engine.SkillMatch{
CandidateSkillID: best.SkillID,
CandidateName: best.Name,
Similarity: decimal.NewFromFloat(bestSim).Round(4),
Matched: true,
}, true
}
func dot(a, b []float32) float64 {
var s float64
for i := range a {
s += float64(a[i]) * float64(b[i])
}
return s
}