Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
48 commits
Select commit Hold shift + click to select a range
ea7e29a
requestor token first auth
kkartunov Aug 25, 2026
d85e4e0
Merge pull request #26 from topcoder-platform/challenges-rag
kkartunov Aug 25, 2026
ca448b5
use v6 ai/sdk for chat
kkartunov Aug 26, 2026
937120a
Merge pull request #27 from topcoder-platform/challenges-rag
kkartunov Aug 26, 2026
233680d
point tc-core-lib
kkartunov Aug 26, 2026
b40c992
Merge pull request #28 from topcoder-platform/challenges-rag
kkartunov Aug 26, 2026
d08a058
revert back to "^2.4.1" for tc-core
kkartunov Aug 26, 2026
19483b6
Merge pull request #29 from topcoder-platform/challenges-rag
kkartunov Aug 26, 2026
38808c3
use v7 ai/sdk for chats
kkartunov Aug 26, 2026
aed313b
Merge pull request #30 from topcoder-platform/challenges-rag
kkartunov Aug 26, 2026
bf8cb2e
set chat path
kkartunov Aug 26, 2026
e57b3d0
Merge pull request #31 from topcoder-platform/challenges-rag
kkartunov Aug 26, 2026
2c12409
use /v6/ai-chat path for chats routes
kkartunov Aug 26, 2026
90a549e
Merge pull request #32 from topcoder-platform/challenges-rag
kkartunov Aug 26, 2026
ef58b0d
wrong LLM id fix
kkartunov Aug 26, 2026
24416a4
Merge pull request #33 from topcoder-platform/challenges-rag
kkartunov Aug 26, 2026
fbf467d
better agent instructions and tools access
kkartunov Aug 26, 2026
5d8d93e
Merge pull request #34 from topcoder-platform/challenges-rag
kkartunov Aug 26, 2026
111d908
VECTOR_SEARCH_THRESHOLD 0.25 by default
kkartunov Aug 26, 2026
a3b86e9
Merge pull request #35 from topcoder-platform/challenges-rag
kkartunov Aug 26, 2026
0704138
markdown support
kkartunov Aug 26, 2026
8bf5760
Merge pull request #36 from topcoder-platform/challenges-rag
kkartunov Aug 26, 2026
d4e2a00
gen thread titles
kkartunov Aug 27, 2026
9391e56
namespace fix, npm update & tools
kkartunov Aug 27, 2026
53092f1
grant access to fetch challenge by id
kkartunov Aug 27, 2026
32958c4
project links
kkartunov Aug 27, 2026
01e4964
add adr 4
kkartunov Aug 27, 2026
7b17b6c
Merge pull request #37 from topcoder-platform/challenges-rag
kkartunov Aug 27, 2026
88e9b2a
bedrock request metadata
kkartunov Aug 28, 2026
76ac6c9
adr 3 - enable cache
kkartunov Aug 28, 2026
16b4c0d
Merge pull request #38 from topcoder-platform/challenges-rag
kkartunov Aug 28, 2026
edcf7a5
PM-6000 - RBAC for agents, workflows, tools
vas3a Sep 1, 2026
e4d1bb8
Best-effort extraction of a JSON object
kkartunov Sep 9, 2026
efb7de9
Merge pull request #42 from topcoder-platform/json-extraction
kkartunov Sep 9, 2026
953c9fc
Revert "Best-effort extraction of a JSON object -> dev"
kkartunov Sep 9, 2026
61d2adf
Merge pull request #45 from topcoder-platform/revert-42-json-extraction
kkartunov Sep 9, 2026
ca3cefb
Merge pull request #47 from topcoder-platform/master
kkartunov Sep 9, 2026
e2a7c3d
bad/truncated structured-output fix
kkartunov Sep 9, 2026
918e127
Merge pull request #48 from topcoder-platform/bad-structured-output
kkartunov Sep 9, 2026
077a41d
Removed the prepareStep attempt
kkartunov Sep 9, 2026
4908149
Merge pull request #49 from topcoder-platform/bad-structured-output
kkartunov Sep 9, 2026
f44792c
Merge branch 'develop' of github.com:topcoder-platform/tc-ai-api into…
vas3a Sep 10, 2026
9604bff
Merge pull request #40 from topcoder-platform/PM-6000_rbac-for-agenst…
vas3a Sep 10, 2026
b800ab2
PM-5999 - RAG management routes
vas3a Sep 4, 2026
f3582f6
Deployment script
vas3a Sep 4, 2026
0ac5440
PM-5999 - Update routes prefix
vas3a Sep 4, 2026
3ca8ce1
PM-5999 - use /ai-api prefix
vas3a Sep 6, 2026
e8b85a0
Merge pull request #39 from topcoder-platform/PM-5999_rag-management-…
vas3a Sep 10, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
53 changes: 28 additions & 25 deletions .circleci/config.yml
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ version: 2.1

defaults: &defaults
docker:
- image: cimg/python:3.13.2-browsers
- image: cimg/python:3.13.2-browsers
install_dependency: &install_dependency
name: Installation of build and deployment dependencies.
command: |
Expand Down Expand Up @@ -35,41 +35,44 @@ builddeploy_steps: &builddeploy_steps

jobs:
# Build & Deploy against development backend
"build-dev":
'build-dev':
!!merge <<: *defaults
environment:
DEPLOY_ENV: "DEV"
LOGICAL_ENV: "dev"
APPNAME: "tc-ai-api"
DEPLOY_ENV: 'DEV'
LOGICAL_ENV: 'dev'
APPNAME: 'tc-ai-api'
DEPLOYMENT_ENVIRONMENT: 'dev'
steps: *builddeploy_steps

"build-prod":
'build-prod':
!!merge <<: *defaults
environment:
DEPLOY_ENV: "PROD"
LOGICAL_ENV: "prod"
APPNAME: "tc-ai-api"
DEPLOY_ENV: 'PROD'
LOGICAL_ENV: 'prod'
APPNAME: 'tc-ai-api'
DEPLOYMENT_ENVIRONMENT: 'prod'
steps: *builddeploy_steps

workflows:
version: 2
build:
jobs:
# Development builds are executed on "develop" branch only.
- "build-dev":
context: org-global
filters:
branches:
only:
- develop

# Production builds are exectuted only on tagged commits to the
# master branch.
- "build-prod":
context: org-global
filters:
branches:
only:
- master
# Development builds are executed on "develop" branch only.
- 'build-dev':
context: org-global
filters:
branches:
only:
- develop
- challenges-rag
tags:
only: /^dev-.*/

# Production builds are exectuted only on tagged commits to the
# master branch.
- 'build-prod':
context: org-global
filters:
branches:
only:
- master
24 changes: 22 additions & 2 deletions .env.sample
Original file line number Diff line number Diff line change
Expand Up @@ -24,9 +24,29 @@ MASTRA_STUDIO_PATH="[ABS PATH_TO_MASTRA_STUDIO_OUTPUT_FOLDER]"
RAG_EMBEDDING_PROVIDER="[TC-Ollama|AWSBedrock — default TC-Ollama]"
RAG_EMBEDDING_MODEL_ID="[nomic-embed-text|amazon.titan-embed-text-v2:0 — default nomic-embed-text]"
VECTOR_INDEX_NAME="[SQL identifier — default challenge_embeddings]"
VECTOR_SEARCH_THRESHOLD="[0-1 — default 0.5]"
VECTOR_SEARCH_THRESHOLD="[0-1 — default 0.25]"
RAG_CHUNK_MAX_SIZE="[characters — default 512]"
RAG_CHUNK_OVERLAP="[characters — default 50]"
RAG_TOP_K="[default 10]"
CHALLENGE_SEARCH_AI_PROVIDER="[TC-Ollama|WiproAI|AWSBedrock|OpenAI — default AWSBedrock]"
CHALLENGE_SEARCH_AI_MODEL_ID="[default us.anthropic.claude-haiku-4-5]"
CHALLENGE_SEARCH_AI_MODEL_ID="[default us.anthropic.claude-haiku-4-5]"
BEDROCK_PROMPT_CACHE_ENABLED="[true|false — default true]"
BEDROCK_PROMPT_CACHE_TTL="[5m|1h — default 5m]"

# ============== Access control (all optional, defaults shown) ==============
# See README "Access control" and docs/adr/0004-role-based-access-for-agents-workflows-tools.md
ACCESS_CONTROL_DEFAULT_POLICY="[public|deny — default public]"
ACCESS_CONTROL_ROLES_CLAIM="[JWT claim key for member roles — default https://<TC_API_BASE domain>/roles]"

# Per-target override — only needed to diverge from the code default / global default.
# <CATEGORY> = AGENT | WORKFLOW | TOOL, <TARGET_KEY> = the target's own .id, upper-snake-cased.
# ACCESS_POLICY_<CATEGORY>_<TARGET_KEY>_MODE="[public|deny — omit to use ROLES/SCOPES below]"
# ACCESS_POLICY_<CATEGORY>_<TARGET_KEY>_ROLES="[comma-separated member roles]"
# ACCESS_POLICY_<CATEGORY>_<TARGET_KEY>_SCOPES="[comma-separated M2M scopes]"
#
# The two ingestion workflows are restricted by DEFAULT_ACCESS_POLICIES in code, so the
# following are redundant unless you are overriding them (e.g. loosening for staging):
# ACCESS_POLICY_WORKFLOW_CHALLENGE_INGESTION_ROLES="administrator"
# ACCESS_POLICY_WORKFLOW_CHALLENGE_INGESTION_SCOPES="challengesRAG:admin"
# ACCESS_POLICY_WORKFLOW_CHALLENGE_BULK_INGESTION_ROLES="administrator"
# ACCESS_POLICY_WORKFLOW_CHALLENGE_BULK_INGESTION_SCOPES="challengesRAG:admin"
258 changes: 227 additions & 31 deletions README.md

Large diffs are not rendered by default.

18 changes: 11 additions & 7 deletions docs/adr/0001-integrate-challenges-vector-rag.md
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,7 @@ Neither can be copied as-is.
| D9 | Event-driven ingestion (triggering on challenge activation/update) is **deferred**; the first release is on-demand invocation only. | Keeps the initial surface small; the workflow is already idempotent per challenge, so an event trigger can be layered on later without reworking it. |
| D10 | Metadata carries **only `projectId`** as an opaque project reference (nullable, stored as a string). No project attributes are denormalized into metadata, no project text is indexed, and ingestion makes no call to projects-api. Consumers that need project detail resolve it in a **subsequent** call to `GET /v6/projects/:projectId`. | Keeps ingestion dependent on a single upstream API, and eliminates the denormalization staleness problem outright: there is no copied project field that can drift when a project is renamed, re-typed, or reassigned to a different billing account, so no refresh mechanism, no re-ingestion trigger, and no staleness signal are needed. It also keeps customer-identifying commercial data out of a store searched by similarity, leaving authorization where it belongs — on the projects-api call, which is already scope-guarded. `projectId` alone still supports project-scoped filtering (`{ projectId: { $in: [...] } }`) and roll-up of challenge hits by project. |
| D11 | The **Challenge Search API** (`GET /v6/challenges`) is the primary bulk ingestion source, not CSV. It supports `projectId`/`projectIds`, `status`, `approvalStatus`, `types`/`tracks`, `tags`/`groups`, `updatedDateStart`/`updatedDateEnd`, and `page`/`perPage` pagination, enabling project-scoped fan-out, status-filtered corpus building, and incremental sync by `updatedDateStart`. CSV backfill remains as a secondary path for offline/air-gapped environments. | The search endpoint is already M2M-authenticated (`scopes: [READ]`) and returns the full challenge payload (including `description`) when `isLightweight` is false (the default). It eliminates the need to export and ship CSV files, and its `updatedDateStart` filter makes incremental sync a single paginated call rather than a full re-export. |
| D12 | `type` and `track` are stored and filtered as **free-form strings**, not Zod enums. `ChallengeType` is a reference table (`model ChallengeType` with `name`, `isActive`, `isTask`, `isLegacy`), not an enum — new types can be added at runtime. `ChallengeTrackEnum` has four values (`DESIGN`, `DATA_SCIENCE`, `DEVELOPMENT`, `QUALITY_ASSURANCE`) but the API returns `track.name` (human-readable, e.g. "Quality Assurance"), not the enum value, and tracks can be deactivated via `isActive`. | The prototype hardcoded `['Challenge', 'First2Finish', 'Marathon Match']` as a Zod enum for type and `['Data Science', 'Design', 'Development']` for track, missing `Quality Assurance` and rejecting any future type. Treating both as strings (with the known values documented for reference but not enforced) is forward-compatible with the reference-table model and avoids ingestion failures when a new type or track is added. |
| D12 | `type` and `track` are stored as **free-form strings**, not Zod enums. `ChallengeType` is a reference table (`model ChallengeType` with `name`, `isActive`, `isTask`, `isLegacy`), not an enum — new types can be added at runtime. `ChallengeTrackEnum` has four values (`DESIGN`, `DATA_SCIENCE`, `DEVELOPMENT`, `QUALITY_ASSURANCE`) but the API returns `track.name` (human-readable, e.g. "Quality Assurance"), not the enum value, and tracks can be deactivated via `isActive`. **Amended (2026-08-27):** both the `type` and `track` *query filters* (on `challengeVectorQueryTool` and the `challenge-search` workflow input) are now Zod enums — `type` restricted to `['Challenge', 'Marathon Match']`, `track` restricted to `['Data Science', 'Design', 'Quality Assurance', 'Development']` — a deliberate reversal of the free-form-filter half of this decision for those two callers. Storage/ingestion is unaffected: it still accepts and indexes any `ChallengeType` value (including `First2Finish`, `Task`) and any `track.name`. | The prototype hardcoded `['Challenge', 'First2Finish', 'Marathon Match']` as a Zod enum for type and `['Data Science', 'Design', 'Development']` for track, missing `Quality Assurance` and rejecting any future type. Treating both as strings (with the known values documented for reference but not enforced) is forward-compatible with the reference-table model and avoids ingestion failures when a new type or track is added. The amendment narrows the *query* surface to the values users actually filter by in practice, accepting that a future new `ChallengeType` (or `First2Finish`/`Task`) becomes unfilterable via `type` until the enum is revisited, and that a deactivated or renamed track (`ChallengeTrackEnum`'s `isActive` flag) would need the same revisit for `track` — it does not touch ingestion, so no re-indexing risk. |

## Implementation plan

Expand Down Expand Up @@ -108,11 +108,15 @@ Neither can be copied as-is.
default, per D2) — chunk sizes,
`VECTOR_SEARCH_THRESHOLD`, `VECTOR_INDEX_NAME` (SQL-identifier validated, as in
the original), `RAG_TOP_K`. Per D12, `type` and `track` are **not** hardcoded
enums — the config documents the known `ChallengeTrackEnum` values
(`DESIGN`, `DATA_SCIENCE`, `DEVELOPMENT`, `QUALITY_ASSURANCE`) and the current
`ChallengeType` reference-table names for readability, but the query tool
accepts any string. Database settings reuse `MASTRA_DB_CONNECTION` and
`MASTRA_DB_SCHEMA` (default `ai`).
enums at the storage/config layer — the config documents the known
`ChallengeTrackEnum` values (`DESIGN`, `DATA_SCIENCE`, `DEVELOPMENT`,
`QUALITY_ASSURANCE`) and the current `ChallengeType` reference-table names for
readability but does not enforce them at the storage/config layer. The `type`
and `track` query filters are Zod enums restricted to `['Challenge', 'Marathon
Match']` and `['Data Science', 'Design', 'Quality Assurance', 'Development']`
respectively (D12 amendment, 2026-08-27) — narrower than storage on purpose.
Database settings reuse `MASTRA_DB_CONNECTION` and `MASTRA_DB_SCHEMA` (default
`ai`).
- **`src/utils/providers/embedding-factory.ts`** — `createEmbeddingModel(provider, modelId)`
switch mirroring `createModel`, using `ollama.embedding(modelId)` and
`createBedrockProvider().embedding(modelId)`, logging via `tcAILogger`. Re-exported
Expand Down Expand Up @@ -263,7 +267,7 @@ Supporting changes:
- **`src/mastra/workflows/challenge/challenge-search-workflow.ts`**, id
`challenge-search`, registered under `workflows` — the deterministic path from D8,
with no agent and no LLM call:
- Input `{ query?: string, skills?: string[], type?: string, track?: string, groups?: string[], projectId?: string | string[], groupBy?: 'chunk' | 'challenge' | 'project', topK?: number, minScore?: number }`.
- Input `{ query?: string, skills?: string[], type?: 'Challenge' | 'Marathon Match', track?: 'Data Science' | 'Design' | 'Quality Assurance' | 'Development', groups?: string[], projectId?: string | string[], groupBy?: 'chunk' | 'challenge' | 'project', topK?: number, minScore?: number }` (`type`/`track` enums per the D12 amendment above).
Filters are supplied explicitly by the caller; unlike the agent path, nothing is
inferred from natural language.
- Single step `search-challenges` executing `challengeVectorQueryTool` with the
Expand Down
Loading