feat: CI/CD pipeline + Learning/Medicine/Travel services
Some checks failed
Build and Deploy GooSeek / build-backend (push) Failing after 1m4s
Build and Deploy GooSeek / build-webui (push) Failing after 1m2s
Build and Deploy GooSeek / deploy (push) Has been skipped

- Add Gitea Actions workflow for automated build & deploy
- Add K8s manifests: webui, travel-svc, medicine-svc, sandbox-svc
- Update kustomization for localhost:5000 registry
- Add ingress for gooseek.ru and api.gooseek.ru
- Learning cabinet with onboarding, courses, sandbox integration
- Medicine service with symptom analysis and doctor matching
- Travel service with itinerary planning
- Server setup scripts (NVIDIA/CUDA, K3s, Gitea runner)

Made-with: Cursor
This commit is contained in:
home
2026-03-02 20:25:44 +03:00
parent 08bd41e75c
commit ab48a0632b
92 changed files with 15562 additions and 2198 deletions

View File

@@ -39,12 +39,16 @@ func CollectEventsEnriched(ctx context.Context, cfg TravelOrchestratorConfig, br
events := extractEventsWithLLM(ctx, cfg.LLM, brief, rawResults, crawledContent)
events = geocodeEvents(ctx, cfg, events)
events = geocodeEvents(ctx, cfg, brief, events)
events = deduplicateEvents(events)
events = filterFreshEvents(events, brief.StartDate)
// Hard filter: drop events that ended up in another city/country due to ambiguous geocoding.
destGeo := geocodeDestinations(ctx, cfg, brief)
events = filterEventsNearDestinations(events, destGeo, 250)
if len(events) > 15 {
events = events[:15]
}
@@ -425,28 +429,74 @@ func tryPartialEventParse(jsonStr string) []EventCard {
return events
}
func geocodeEvents(ctx context.Context, cfg TravelOrchestratorConfig, events []EventCard) []EventCard {
func geocodeEvents(ctx context.Context, cfg TravelOrchestratorConfig, brief *TripBrief, events []EventCard) []EventCard {
destSuffix := strings.Join(brief.Destinations, ", ")
for i := range events {
if events[i].Address == "" || (events[i].Lat != 0 && events[i].Lng != 0) {
continue
}
geoCtx, cancel := context.WithTimeout(ctx, 5*time.Second)
geo, err := cfg.TravelData.Geocode(geoCtx, events[i].Address)
cancel()
queries := []string{events[i].Address}
if destSuffix != "" && !strings.Contains(strings.ToLower(events[i].Address), strings.ToLower(destSuffix)) {
queries = append(queries, fmt.Sprintf("%s, %s", events[i].Address, destSuffix))
}
queries = append(queries, fmt.Sprintf("%s, %s", events[i].Title, destSuffix))
if err != nil {
log.Printf("[travel-events] geocode failed for '%s': %v", events[i].Address, err)
continue
var lastErr error
for _, q := range queries {
geoCtx, cancel := context.WithTimeout(ctx, 5*time.Second)
geo, err := cfg.TravelData.Geocode(geoCtx, q)
cancel()
if err != nil {
lastErr = err
continue
}
events[i].Lat = geo.Lat
events[i].Lng = geo.Lng
break
}
events[i].Lat = geo.Lat
events[i].Lng = geo.Lng
if events[i].Lat == 0 && events[i].Lng == 0 {
if lastErr != nil {
log.Printf("[travel-events] geocode failed for '%s': %v", events[i].Address, lastErr)
} else {
log.Printf("[travel-events] geocode failed for '%s'", events[i].Address)
}
continue
}
}
return events
}
func filterEventsNearDestinations(events []EventCard, destinations []destGeoEntry, maxKm float64) []EventCard {
if len(destinations) == 0 {
return events
}
filtered := make([]EventCard, 0, len(events))
for _, e := range events {
if e.Lat == 0 && e.Lng == 0 {
continue
}
minD := 1e18
for _, d := range destinations {
if d.Lat == 0 && d.Lng == 0 {
continue
}
dd := distanceKm(e.Lat, e.Lng, d.Lat, d.Lng)
if dd < minD {
minD = dd
}
}
if minD <= maxKm {
filtered = append(filtered, e)
} else {
log.Printf("[travel-events] dropped far event '%s' (%.0fkm from destinations)", e.Title, minD)
}
}
return filtered
}
func deduplicateEvents(events []EventCard) []EventCard {
seen := make(map[string]bool)
var unique []EventCard