This document is the single source-of-truth for implementation notes, edits, and decisions made while building MajorMatch. Use this file to record what you implemented, why, and any follow-up tasks.
When you make a change, add a new entry at the top with the following fields:
- Date (YYYY-MM-DD)
- Author (initials or name)
- Area (UI / Backend / Data / Infra / Docs / ML)
- Files changed (workspace-relative paths)
- Summary: one-line description
- Details: optional short paragraph with links and next steps
Date: 2026-05-25
Author: RV
Area: Scaffold
Files changed:
- streamlit_app.py
- api/predict.py
- api/search.py
- data/courses.csv
- requirements.txt
- scripts/embed.py
Summary: Added initial repo scaffold and app stub
Details: Created minimal Streamlit app, prediction/search stubs, and example course corpus. Next: wire real embeddings and the ML model.
Date: 2026-05-26 Author: RV Area: Backend / UI / Validation Files changed:
- api/orchestrator.py
- tests/test_orchestrator.py
- docs/IMPLEMENTATION.md
Summary: Preserve spaces in streamed assistant replies
Details: The orchestrator now forwards streamed assistant chunks without stripping leading whitespace, so Ollama fragments like
" on"stay readable when concatenated in the Streamlit chat UI. Added a regression test that verifies the streamed reply keeps spaces and line breaks intact.
Date: 2026-05-26 Author: RV Area: Backend / UI / Validation Files changed:
- api/ollama.py
- tests/test_ollama.py
- docs/IMPLEMENTATION.md
Summary: Fix streamed Ollama replies rendering raw NDJSON instead of assistant text
Details: Updated
chat_completion_stream()to parse Ollama's newline-delimited JSON stream and yield only the assistantmessage.contentfragments. Added a regression test that verifies the streamed output is plain text, which prevents the Streamlit chat UI from showing raw JSON payloads during assistant responses.
Date: 2026-05-25 Author: RV Area: Scaffold Files changed:
- streamlit_app.py
- api/predict.py
- api/search.py
- data/courses.csv
- requirements.txt
- scripts/embed.py
Summary: Initial scaffold added (Streamlit app, API stubs, sample data).
Details: Vertical MVP stub implemented to allow end-to-end testing without external services. Teammate will add the serialized ML model under
models/or expose an API; embeddings will be computed and stored in PostgreSQL withpgvectorlater.
Date: 2026-05-25 Author: RV Area: MVP Files changed:
- api/search.py
Summary: Add embedding-backed semantic search with fallback
Details:
api/search.pynow attempts to loadsentence-transformersand compute corpus embeddings on demand. If the library or model fails to load, the code falls back to the earlier keyword scoring method. This enables a stronger vertical MVP while keeping the repo runnable without heavy dependencies.
Date: 2026-05-25 Author: RV Area: UI Files changed:
- app_logic.py
- streamlit_app.py
- scripts/smoke_test.py
Summary: Reworked the Streamlit MVP into a clearer end-to-end flow
Details: The UI now guides users through profile input, career-track recommendation, and course discovery in one screen. The core logic moved into
app_logic.pyso it can be smoke-tested independently withscripts/smoke_test.py.
Date: 2026-05-25 Author: RV Area: Validation Files changed:
- app_logic.py
- streamlit_app.py
- scripts/smoke_test.py
- api/search.py
- api/predict.py Summary: Syntax validation passed on the edited Python files Details: The terminal smoke test was skipped by the environment, so I validated the edited Python files through the editor error checker. No syntax or static errors were reported for the current MVP slice.
Date: 2026-05-25 Author: RV Area: Backend / UI Files changed:
- app_logic.py
- api/ollama.py
- streamlit_app.py Summary: Added an optional Ollama interview flow with slider fallback Details: The app can now ask the user questions through Ollama, build a structured coding/math/design profile, and still keep the slider controls inside a debug expander. If Ollama is unavailable, the interview falls back to local prompts so the MVP stays demoable.
Date: 2026-05-25 Author: RV Area: Backend / UI Files changed:
- api/ollama.py
- streamlit_app.py
Summary: Auto-resolve an installed Ollama chat model instead of hardcoding a missing default
Details: The adapter now prefers installed chat models from
ollama listand defaults tollama3.2:1b, which matches the model already present in your environment. The Streamlit UI also shows which chat model is being used.
Date: 2026-05-25 Author: RV Area: Backend / UI Files changed:
- api/ollama.py Summary: Suppress brace-only Ollama replies and infer profile updates from user text Details: The interview flow now extracts useful profile signals from the user’s natural-language response before calling Ollama, and it replaces malformed brace-only assistant output with a real follow-up question based on missing fields.
Date: 2026-05-25 Author: RV Area: Backend / UI Files changed:
- api/ollama.py Summary: Switch Ollama interview from JSON-only output to natural chat replies Details: The assistant now talks in plain text, while the app still infers and merges profile scores separately. This avoids brace-only replies and keeps the chat layer usable even when the model does not strictly follow a JSON format.
Date: 2026-05-25 Author: RV Area: Backend / UI Files changed:
- api/ollama.py
- streamlit_app.py Summary: Remove Ollama fallbacks so debug failures surface directly Details: The chat flow now raises errors when Ollama is unavailable or returns unusable output. The UI no longer tells the user there is a silent fallback path; it now reports that Ollama must be running for chat debugging.
Date: 2026-05-25 Author: RV Area: Backend / Data / UI Files changed:
- course_index.py
- api/search.py
- app_logic.py
- streamlit_app.py
- scripts/embed.py
- requirements.txt Summary: Added pgvector-backed embeddings, semantic search, and PCA/UMAP/t-SNE projections Details: Course records are now stored in PostgreSQL with pgvector embeddings and 2D coordinates for PCA, UMAP, and t-SNE. The Streamlit app can rebuild the index from the CSV corpus and render a projection scatter plot for any of the three methods.
Date: 2026-05-25 Author: RV Area: Backend / Data / Infra Files changed:
- course_index.py
- scripts/embed.py
- api/search.py
- api/ollama.py
- app_logic.py
- streamlit_app.py Summary: Make embeddings robust across developer environments and fix import/indexing issues Details: Attempts to enable the vector extension on the DB but falls back to storing embeddings as float[] (ARRAY(Float)) when the extension is unavailable. Similarity search now uses a Python cosine-similarity fallback (loading stored embeddings into memory) so search works without pgvector. The course indexer and embed.py were refactored so the database is created/initialized before use and the embed script can import project modules reliably.
Date: 2026-05-25 Author: RV Area: Backend / Data / API Files changed:
- course_index.py
- api/search.py
- scripts/embed.py
- app_logic.py
- streamlit_app.py
- docs/IMPLEMENTATION.md
Summary: Implement semantic search API and make embedding/indexing robust
Details: Implemented a stable
semantic_search()API wrapper that validates inputs, strips embeddings from responses, and returns consistent metadata (id,title,description,score,score_normalized, and projection coordinates). Refactored embedding storage to usefloat[](ARRAY(Float)) for portability, added a Python cosine-similarity fallback when server-sidepgvectoris unavailable, and updated the indexer (scripts/embed.py) to support indexing multiple CSV files underdata/. Also fixed import issues by ensuring the embed script can import project modules and added CSV header expectations to the docs. Next: add smoke tests and UI polish for provider/fallback visibility.
Date: 2026-05-25 Author: RV Area: UI / Backend Files changed:
- course_index.py
- streamlit_app.py
- docs/IMPLEMENTATION.md
Summary: Show query point and highlight top-match in projection plot
Details: Added
project_courses_with_query()tocourse_index.pyto compute 2D projections that include the user's query so points share a consistent coordinate system. Updatedstreamlit_app.pyto render the query point on the projection and color the top match differently (colors: course=blue, top_match=red, query=green). Also surfaced embedding model name andpgvectoravailability in the UI so users can see whether the app is using the in-Python similarity fallback or server vector ops. Note: PCA/UMAP/TSNE projections are computed on-demand and may add CPU cost for large corpora; next steps include annotating the top match, increasing query marker size, and caching projection reducers for faster UI response.
Date: 2026-05-25 Author: RV Area: Docs / Validation Files changed:
- tests/test_course_index.py
- tests/test_api_search.py
- tests/README.md
- requirements.txt
Summary: Add a dedicated tests directory with pytest coverage for corpus loading and semantic search
Details: Created a new
tests/package with lightweight pytest checks that cover CSV corpus loading, thesemantic_search()result shape, and the query projection helper. Addedpytesttorequirements.txtand verified the suite withpython -m pytestinside the project virtual environment. The tests are intentionally isolated from the database by monkeypatching the core helpers so they run quickly and reliably in a capstone-sized corpus.
Date: 2026-05-25 Author: RV Area: Backend / UI / Data Files changed:
- api/jobs.py
- app_logic.py
- streamlit_app.py
- tests/test_jobs.py
Summary: Integrate an Adzuna-backed career context API into the user flow
Details: Added a small job-market adapter in
api/jobs.pythat queries Adzuna usingADZUNA_APP_IDandADZUNA_APP_KEY, derives a stable career-context payload (job_count, salary range, top job titles, top companies), and degrades gracefully when credentials are missing. The Streamlit app now renders a dedicated career-context section immediately after the track recommendation so the user can see market data before exploring courses. Added pytest coverage for the happy path and the missing-credential fallback. Next: wire the same career-context tool into the Ollama orchestrator so the assistant can decide when to call it.
Date: 2026-05-25 Author: RV Area: Backend / UI / ML Files changed:
- api/ollama.py
- api/orchestrator.py
- streamlit_app.py
- tests/test_orchestrator.py
Summary: Add an Ollama tool-calling orchestrator for prediction, job context, and course search
Details: Introduced a shared Ollama
chat_completion()helper that supports tool definitions, then addedapi/orchestrator.pyto run a tool-calling conversation loop using the three main MajorMatch capabilities: career-track prediction, Adzuna job context, and semantic course search. The Streamlit app now includes a dedicated orchestrated-assistant panel so users can ask one question and let Ollama decide which tools to invoke. Added pytest coverage that simulates a multi-tool conversation and verifies the tool trace and returned artifacts. Next: refine the prompt/tool schema and consider showing orchestrator artifacts in a more compact UI card layout.
Date: 2026-05-25 Author: RV Area: UI / Backend Files changed:
- app_logic.py
- streamlit_app.py
- docs/IMPLEMENTATION.md
Summary: Switch to chatbot-first UI and show tool-specific interfaces only when tools are actually invoked
Details: Reworked the Streamlit experience to match a chat-native flow similar to ChatGPT/Gemini behavior. The app now starts as a pure chatbot and does not pre-render prediction, career-context, course, or visualization sections by default. Added deterministic intent routing in
app_logic.pyso tools are called only when the user message clearly asks for those features (to reduce hallucination risk with lightweight local models). Tool-specific UIs are now conditionally rendered only when that tool was used for the latest user request.
Date: 2026-05-25 Author: RV Area: UI / Backend Files changed:
- streamlit_app.py
- app_logic.py
- docs/IMPLEMENTATION.md Summary: Switched to a user-selected tool flow and bundled semantic search with visualization Details: Replaced model-driven tool triggering with a visible picker so the user now decides when a tool runs. Grouped course search and visualization into one semantic-search action, and changed assistant replies to be grounded in the selected tool result while preserving the prior chat context. Next: continue refining the picker presentation and the exact reply format for each tool.
Date: 2026-05-26 Author: RV Area: UI / Backend / ML Files changed:
- api/orchestrator.py
- streamlit_app.py
- tests/test_orchestrator.py
- docs/IMPLEMENTATION.md Summary: Replaced manual tool selection with autonomous function-calling orchestration Details: Refactored the chat experience back into a true agent flow. The model now chooses tools automatically through Ollama function-calling using explicit schemas for career prediction, career context, and combined semantic search with projection output. The Streamlit UI no longer exposes a tool dropdown; it simply renders the assistant reply and any tool artifacts returned by the orchestrator. Added projection data to the semantic-search tool so the visualization is bundled with the course results.
Date: 2026-05-26 Author: RV Area: UI / Backend / ML Files changed:
- api/orchestrator.py
- tests/test_orchestrator.py
- docs/IMPLEMENTATION.md Summary: Added dedicated post-tool response prompts for each tool Details: The orchestrator now builds tool-specific final reply instructions after tool execution, so career prediction, career context, and semantic search each get a tailored response style. The final assistant message is still generated from the shared chat history, but the prompt now reflects the exact tool results that were produced. Updated the orchestrator test to cover the extra finalization pass.
Date: 2026-05-26 Author: RV Area: UI / Backend / ML Files changed:
- api/orchestrator.py
- docs/IMPLEMENTATION.md Summary: Make semantic-search replies mirror the database output more strictly Details: Tightened the semantic-search post-tool prompt so the final assistant message starts from the tool’s database-backed results instead of paraphrasing them. This reduces wording drift between the tool output and the model response; the reply now uses a fixed database-summary frame and only mentions the visualization if it is available.
Date: 2026-05-26 Author: RV Area: UI / Backend / ML Files changed:
- api/orchestrator.py
- tests/test_orchestrator.py
- docs/IMPLEMENTATION.md Summary: Make career-context replies mirror the API output more strictly Details: Similar to the semantic-search prompt tightening, the career-context post-tool prompt now instructs the model to report only the exact API results (job count, salary range, top job titles, top companies) in a fixed database-summary format. This reduces hallucination risk and keeps the assistant reply closely tied to the actual data returned by the tool. Updated the orchestrator test to expect the new career-context reply format.
Date: 2026-05-26 Author: RV Area: UI / Backend / ML Files changed:
- api/orchestrator.py
- tests/test_orchestrator.py
- docs/IMPLEMENTATION.md Summary: Make the normal AI response more user-friendly when no tools are invoked Details: Added explicit no-tool instructions for greetings, identity questions, and other normal chat so the assistant responds directly instead of invoking tools. Updated the Streamlit welcome copy to introduce MajorMatch in plain language and added a test that locks the no-tool path for casual questions.
Date: 2026-05-26 Author: RV Area: UI / Backend / ML Files changed:
- api/orchestrator.py
- tests/test_orchestrator.py
- docs/IMPLEMENTATION.md Summary: Added a friendly identity reply for common self-introduction questions Details: The orchestrator now detects common user questions about the assistant's identity (e.g., "who are you?", "introduce yourself") and responds with a friendly introduction instead of a generic greeting. This adds a bit of personality to the assistant and makes the chat feel more engaging. Updated the test to expect the new identity reply when those questions are asked.
Date: 2026-05-26 Author: RV Area: UI / Backend / ML Files changed:
- api/orchestrator.py
- tests/test_orchestrator.py
- docs/IMPLEMENTATION.md
Summary: Robust tool-argument parsing and sanitization; fixed UI header leakage
Details: Improved
_parse_tool_arguments()inapi/orchestrator.pyto decode nested and serialized parameter shapes (handles JSON strings inobject/parametersfields and flattens single-item lists), ensuring tools receive the intended keys (track,location,user_query,top_k). Left_clean_assistant_text()and deterministic greeting/identity bypasses intact. Updated tests and validated the change by running the test suite — all tests pass (11 passed). Next: add a targeted unit test for malformed tool-call shapes and optionally add runtime logging for malformed calls.
Date: 2026-05-26 Author: RV Area: UI / Backend / ML Files changed:
- app_logic.py
- api/search.py
- api/orchestrator.py
- streamlit_app.py Summary: Cap semantic search at five results and add a projection-method selector Details: Semantic search now returns at most five matches across the helper, API wrapper, and orchestrated tool path. The semantic-search response also carries PCA, UMAP, and t-SNE projection variants so the Streamlit view can let the user switch the plot method directly.
Date: 2026-05-26 Author: RV Area: UI / Backend / ML Files changed:
- api/predict.py
- tests/test_predict.py
Summary: Replace the predictor stub with the random-forest model
Details:
predict_track()now loads or retrains the classifier fromml_model/stud_training.csv, converts the current coding/math/design profile into the model's interest feature space, and maps the predicted major back into the app's existing track labels. Added tests that cover both the model-backed path and the fallback path.
Date: 2026-05-26 Author: RV Area: UI / Backend / ML Files changed:
- api/predict.py
- api/jobs.py
- app_logic.py
- streamlit_app.py
- tests/test_predict.py
- tests/test_orchestrator.py Summary: Expose raw model labels from the classifier instead of collapsing to three tracks Details: The prediction pipeline now returns the model's actual class label, plus a separate compatibility category for routing. The predictor no longer hardcodes the old three-track collapse, and the job-query builder now handles the broader label space from the dataset. Updated the UI and tests to display and assert the raw label path.
Date: 2026-05-26 Author: RV Area: UI / Backend / ML Files changed:
- streamlit_app.py
- api/predict.py
- tests/test_predict.py Summary: Add a dedicated prediction tool panel driven by multiselect feature input Details: The chat flow now opens a separate prediction panel when the user asks for the tool, instead of forcing the assistant to infer a prediction from free-form text. The panel mirrors the teammate prototype by letting the user pick interests from the model's feature list and then runs the classifier directly on those selections.
Date: 2026-05-26 Author: RV Area: UI / Backend / ML Files changed:
- api/predict.py
- app_logic.py
- streamlit_app.py
- tests/test_predict.py
- docs/IMPLEMENTATION.md
Summary: Extend
predict_trackto return and display top-3 predictions Details: Addedtop_predictionsoutput topredict_track()using model probabilities (top 3 ranked labels with confidence and category), while keeping the existing primarylabel/confidencefields for compatibility. Added fallback behavior that returns a single-itemtop_predictionslist when the model path is unavailable. Propagated the new field throughrecommend_track()and updated the prediction UI card to show the ranked results. Expanded predictor tests to assert the new payload shape. Validation: full test suite passes (14 passed).
Date: 2026-05-26 Author: RV Area: UI / Backend Files changed:
- streamlit_app.py
Summary: Render only the most recent tool artifact in the UI
Details: Updated the chat UI to show a single tool panel per assistant turn. The app now tracks
assistant_latest_tool_namein session state and clears previous artifacts at the start of each new user turn so stale panels are not shown after timeouts or unrelated requests. The code chooses the most recent tool from the orchestratortool_tracethat produced an artifact and renders only that panel; a safe fallback renders one available artifact when the marker is missing. Tests were run after the change and the suite passed (14 passed).
Date: 2026-05-26 Author: RV Area: Backend / Docs Files changed:
- api/ollama.py
- scripts/test_ollama_tools.py Summary: Surface Ollama HTTP error bodies and fall back to a tool-capable model when tools are requested Details: Added enhanced error handling in api/ollama.py to read and surface HTTP error bodies from the Ollama server so validation failures (e.g., "does not support tools") are visible to the developer. Implemented a fallback selection in chat_completion() that prefers an available model known to support function/tool calling when the request includes tools. Added scripts/test_ollama_tools.py to reproduce the issue locally and validate the fallback. Verified the repro script locally; tool-enabled requests now succeed by falling back to llama3.2:1b in the tested environment.
Date: 2026-05-26 Author: RV Area: Backend / Performance / Docs Files changed:
- course_index.py
- api/orchestrator.py
- tests/test_orchestrator.py Summary: Cache the course corpus and remove duplicate semantic-search embedding work Details: Added in-memory caching for the loaded course corpus and introduced a combined search-plus-projection helper so each semantic-search request reuses one corpus load and one query embedding pass. Updated the orchestrator to call the combined helper instead of running separate search and projection passes, and adjusted the orchestrator test to stub the new fast path. Verified the optimization with the full test suite (14 passed).
Date: 2026-05-26 Author: RV Area: Backend / UX / Docs Files changed:
- api/ollama.py
- api/orchestrator.py
- streamlit_app.py
Summary: Add chunked streaming for assistant replies and Streamlit integration
Details: Implemented
chat_completion_stream()inapi/ollama.pyto POST withstream=Trueand yield incremental text chunks. Updatedapi/orchestrator.pyto accept astream_chat_fnandon_stream_chunkcallback so the final assistant reply can be streamed when available. Integrated streaming intostreamlit_app.pyby adding a placeholder chat message and a chunk handler that updates the UI incrementally as chunks arrive. Verified with the test suite (14 passed) and manual runs against the local Ollama instance.
-
Keep entries short and chronological (newest at top).
-
Link to specific files when referring to code (use workspace-relative paths).
-
CSV header expectations: each CSV indexed by
scripts/embed.pymust includetitleanddescriptioncolumns (case-insensitive). Rows missing those fields are skipped; use UTF-8 encoding and standard CSV quoting.