Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
3 changes: 2 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -6,4 +6,5 @@ dist
.env
*.db
*.db-*
.vscode
.vscode
test-data
2 changes: 1 addition & 1 deletion .nvmrc
Original file line number Diff line number Diff line change
@@ -1 +1 @@
v24.13.0
v24.14.0
2 changes: 1 addition & 1 deletion Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

FROM node:24.13.0-alpine

RUN apk add --no-cache bash
RUN apk add --no-cache bash git
RUN apk update
Copy link

Copilot AI Mar 25, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

apk add --no-cache already avoids caching the index; running apk update afterwards is redundant and adds an extra layer/time. Consider removing RUN apk update (or combine update+add in a single layer without --no-cache if you intentionally need the index cache).

Suggested change
RUN apk update

Copilot uses AI. Check for mistakes.

WORKDIR /app
Expand Down
28 changes: 15 additions & 13 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
"main": "index.js",
"scripts": {
"test": "vitest run",
"test:integration": "vitest run --config vitest.integration.config.ts",
"dev": "mastra dev",
"build": "mastra build",
"start": "mastra start",
Expand All @@ -25,26 +26,27 @@
},
"dependencies": {
"@mastra/auth-auth0": "^1.0.1",
"@mastra/core": "^1.10.0",
"@mastra/core": "^1.15.0",
"@mastra/evals": "^1.1.2",
"@mastra/libsql": "^1.6.4",
"@mastra/loggers": "^1.0.2",
"@mastra/memory": "^1.6.1",
"@mastra/observability": "^1.3.1",
"@mastra/pg": "^1.7.2",
"ai": "^6.0.105",
"ai-sdk-ollama": "^3.8.0",
"@mastra/libsql": "^1.7.1",
"@mastra/loggers": "^1.0.3",
"@mastra/memory": "^1.9.0",
"@mastra/observability": "^1.5.1",
"@mastra/pg": "^1.8.2",
"@topcoder/wipro-ai-sdk-provider": "git+https://git.topcoder.com/Topcoder-Platform/Wipro-Provider-AI-SDK.git",
Copy link

Copilot AI Mar 25, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Using a git URL without a pinned tag/commit makes builds non-reproducible (the dependency can change without a package.json change). For production releases, pin to a specific commit SHA or a versioned tag to ensure deterministic installs and easier rollbacks.

Suggested change
"@topcoder/wipro-ai-sdk-provider": "git+https://git.topcoder.com/Topcoder-Platform/Wipro-Provider-AI-SDK.git",
"@topcoder/wipro-ai-sdk-provider": "git+https://git.topcoder.com/Topcoder-Platform/Wipro-Provider-AI-SDK.git#v1.0.0",

Copilot uses AI. Check for mistakes.
"ai": "^6.0.134",
"ai-sdk-ollama": "^3.8.1",
"tc-core-library-js": "^2.4.1",
"zod": "^4.3.6"
},
"devDependencies": {
"@eslint/js": "^10.0.1",
"@types/node": "^25.3.3",
"eslint": "^10.0.2",
"mastra": "^1.3.7",
"@types/node": "^25.5.0",
"eslint": "^10.1.0",
"mastra": "^1.3.14",
"prettier": "^3.8.1",
"typescript": "^5.9.3",
"typescript-eslint": "^8.56.1",
"vitest": "^4.0.18"
"typescript-eslint": "^8.57.1",
"vitest": "^4.1.0"
}
}
1,853 changes: 831 additions & 1,022 deletions pnpm-lock.yaml

Large diffs are not rendered by default.

29 changes: 6 additions & 23 deletions src/mastra/agents/challenge/challenge-parser-agent.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { Agent } from '@mastra/core/agent';
import { ollama } from '../../../utils';
import { wipro } from '../../../utils';

/**
* Master agent responsible for parsing Topcoder challenge specifications.
Expand All @@ -14,28 +14,11 @@ import { ollama } from '../../../utils';
export const challengeParserAgent = new Agent({
id: 'challenge-parser-agent',
name: 'Challenge Specification Parser',
model: ollama('mistral:latest', {
options: {
// Sampling — near-deterministic for strict JSON schema compliance
temperature: 0.1,
top_k: 40,
top_p: 0.9,

// Repetition control — prevent repetitive text across many requirement entries
// and avoid echoing skill instruction phrasing into output
repeat_penalty: 1.15,
repeat_last_n: 192,

// Context window — system prompt (~1.2K tok) + skill activations (~3K tok)
// + full challenge spec (some specs are very long)
num_ctx: 16384,

// Generation limit — enough for structured JSON with many requirements, groups, tech stack, etc.
num_predict: 8192,

// Prompt processing — smaller system prompt with skills loaded progressively
num_batch: 256,
},
model: wipro.chatModel('gpt-5-chat', {
temperature: 0.1,
topK: 40,
topP: 0.9,
maxOutputTokens: 16384,
}),
instructions: {
role: 'system',
Expand Down
53 changes: 53 additions & 0 deletions src/mastra/agents/jd/jd-rewriter-agent.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
import { Agent } from '@mastra/core/agent';
import { wipro } from '../../../utils';

export const jdRewriterAgent = new Agent({
id: 'jd-rewriter-agent',
name: 'Job Description Rewriter',
model: wipro.chatModel('gpt-5-chat', {
temperature: 0.3,
topK: 40,
topP: 0.9,
maxOutputTokens: 16384,
}),
instructions: {
role: 'system',
content: `You are an expert technical recruiter and job description writer for Topcoder.
Your sole job is to take a raw, rough, or vague job description and rewrite it
into a clear, professional, well-structured format suitable for posting as a
Topcoder opportunity.

────────────────────────────────────────────────────────
CONTEXT
────────────────────────────────────────────────────────
Talent managers receive rough or vague job descriptions from customers that
are not in a suitable format for posting. You must rewrite them into a
standardized, professional format ensuring consistency across all
opportunities posted on the Topcoder platform.

────────────────────────────────────────────────────────
SKILLS-DRIVEN REWRITING PROTOCOL
────────────────────────────────────────────────────────
Follow these concerns in order:

1. **jd-content-rewriting** — Rewrite the raw text into clear, professional,
specific language. Follow the rewriting principles, action verb usage,
signal word classification, and sparse/detailed input handling rules.

2. **jd-structure-formatting** — Format the rewritten content into the
canonical Topcoder JD structure. Follow the section order, formatting
rules, and Markdown conventions for the formattedDescription output.

3. **jd-skills-extraction** — Extract skill keywords from the rewritten
content. Follow the canonical casing rules, extraction heuristics, and
compound term splitting rules.

────────────────────────────────────────────────────────
STRICT OUTPUT CONTRACT
────────────────────────────────────────────────────────
Return ONLY the JSON object matching the provided schema.
Do NOT add commentary, markdown fences, or extra keys.
Every field is mandatory per the schema — never omit a key.
/no_think`,
},
});
22 changes: 9 additions & 13 deletions src/mastra/agents/skills/skills-matching-agent.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import { Agent } from '@mastra/core/agent';
import { Memory } from '@mastra/memory';
import { ollama } from '../../../utils/providers/ollama';
import { wipro } from '../../../utils';
import { PostgresStore } from '@mastra/pg';
import { skillDiscoveryScorers } from '../../scorers/skills-matching-scorers';
import { instanceScorers } from '../../scorers/instance-scorers';

export const skillsMatchingAgent = new Agent({
id: 'skillsMatchingAgent',
Expand All @@ -26,27 +26,23 @@ Output requirements:
- No prose, no markdown, no extra keys.
`,
},
model: ollama('mistral:latest', {
options: {
temperature: 0.1,
top_p: 0.5,
repeat_penalty: 1.1,
num_predict: 2048,
},
model: wipro.chatModel('gpt-5-chat', {
temperature: 0.1,
topP: 0.5,
}),
scorers: {
answerRelevancy: {
scorer: skillDiscoveryScorers.skillDiscoveryAnswerRelevancyScorer,
scorer: instanceScorers.instanceAnswerRelevancyScorer,
sampling: {
type: 'ratio',
rate: Number(process.env.SKILL_DISCOVERY_EVAL_SAMPLE_RATE ?? 0.5),
rate: Number(process.env.EVAL_SAMPLE_RATE ?? 0.5),
},
},
promptAlignment: {
scorer: skillDiscoveryScorers.skillDiscoveryPromptAlignmentScorer,
scorer: instanceScorers.instancePromptAlignmentScorer,
sampling: {
type: 'ratio',
rate: Number(process.env.SKILL_DISCOVERY_EVAL_SAMPLE_RATE ?? 0.5),
rate: Number(process.env.EVAL_SAMPLE_RATE ?? 0.5),
},
},
},
Expand Down
21 changes: 14 additions & 7 deletions src/mastra/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,22 +2,24 @@ import { Mastra } from '@mastra/core';
import { Observability, DefaultExporter, SensitiveDataFilter } from '@mastra/observability';
import { skillExtractionWorkflow } from './workflows/skills/skill-extraction-workflow';
import { challengeContextWorkflow } from './workflows/challenge/challenge-context-workflow';
import { jdAutowriteWorkflow } from './workflows/jd/jd-autowrite-workflow';
import { skillsMatchingAgent } from './agents/skills/skills-matching-agent';
import { challengeParserAgent } from './agents/challenge/challenge-parser-agent';
import { jdRewriterAgent } from './agents/jd/jd-rewriter-agent';
import { PostgresStore } from '@mastra/pg';
import {
skillDiscoveryAnswerRelevancyScorer,
skillDiscoveryPromptAlignmentScorer,
} from './scorers/skills-matching-scorers';
instanceAnswerRelevancyScorer,
instancePromptAlignmentScorer,
} from './scorers/instance-scorers';
Copy link

Copilot AI Mar 25, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The imports reference ./scorers/instance-scorers, but the diff only shows changes to src/mastra/scorers/skills-matching-scorers.ts (and no new instance-scorers module). This will fail at build/runtime. Fix by either (a) adding an instance-scorers.ts file that exports these symbols, or (b) updating imports (here and in skills-matching-agent.ts) to import from the actual scorer module file.

Suggested change
} from './scorers/instance-scorers';
} from './scorers/skills-matching-scorers';

Copilot uses AI. Check for mistakes.
import { apiAuthLayer, middlewareConfig, tcAILogger } from '../utils';
import { aiWorkspace } from './workspaces';

export const mastra = new Mastra({
workflows: { skillExtractionWorkflow, challengeContextWorkflow },
agents: { skillsMatchingAgent, challengeParserAgent },
workflows: { skillExtractionWorkflow, challengeContextWorkflow, jdAutowriteWorkflow },
agents: { skillsMatchingAgent, challengeParserAgent, jdRewriterAgent },
scorers: {
skillDiscoveryAnswerRelevancyScorer,
skillDiscoveryPromptAlignmentScorer,
instanceAnswerRelevancyScorer,
instancePromptAlignmentScorer,
},
storage: new PostgresStore({
id: 'tc-ai-api-store',
Expand All @@ -36,6 +38,7 @@ export const mastra = new Mastra({
}),
workspace: aiWorkspace,
server: {
host: process.env.MASTRA_HOST || process.env.HOST || '0.0.0.0',
port: Number(process.env.PORT || 3000),
studioBase: '/studio',
auth: process.env.DISABLE_AUTH === 'true' ? undefined : apiAuthLayer,
Expand All @@ -44,4 +47,8 @@ export const mastra = new Mastra({
},
middleware: middlewareConfig,
},
bundler: {
externals: ["tc-core-library-js"],
transpilePackages: ['@topcoder/wipro-ai-sdk-provider'],
},
});
Original file line number Diff line number Diff line change
Expand Up @@ -6,18 +6,18 @@ import { ollama } from 'ai-sdk-ollama';

const evalModel = ollama(process.env.MASTRA_EVAL_MODEL ?? 'mistral:latest');

export const skillDiscoveryAnswerRelevancyScorer = createAnswerRelevancyScorer({
export const instanceAnswerRelevancyScorer = createAnswerRelevancyScorer({
model: evalModel,
});

export const skillDiscoveryPromptAlignmentScorer = createPromptAlignmentScorerLLM({
export const instancePromptAlignmentScorer = createPromptAlignmentScorerLLM({
model: evalModel,
options: {
evaluationMode: 'user',
},
});

export const skillDiscoveryScorers = {
skillDiscoveryAnswerRelevancyScorer,
skillDiscoveryPromptAlignmentScorer,
export const instanceScorers = {
instanceAnswerRelevancyScorer,
instancePromptAlignmentScorer,
};
Loading
Loading