Skip to content

Commit 5976d6d

Browse files
committed
implement AI labeling to generate description for manifest
1 parent 54e2d1c commit 5976d6d

File tree

21 files changed

+1240
-4
lines changed

21 files changed

+1240
-4
lines changed

deno.json

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,11 @@
66
"lock": false,
77
"imports": {
88
"@inquirer/prompts": "npm:@inquirer/prompts@^7.5.3",
9+
"@langchain/anthropic": "npm:@langchain/anthropic@^0.3.23",
10+
"@langchain/core": "npm:@langchain/core@^0.3.61",
11+
"@langchain/google-genai": "npm:@langchain/google-genai@^0.2.14",
12+
"@langchain/langgraph": "npm:@langchain/langgraph@^0.3.5",
13+
"@langchain/openai": "npm:@langchain/openai@^0.5.15",
914
"@oak/oak": "jsr:@oak/oak@^17.1.4",
1015
"@std/expect": "jsr:@std/expect@^1.0.16",
1116
"@std/path": "jsr:@std/path@^1.0.9",

src/cli/handlers/init/index.ts

Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,11 @@ import {
2020
import { ApiService } from "../../../apiService/index.ts";
2121
import type { globalConfigSchema } from "../../middlewares/globalConfig.ts";
2222
import { isAuthenticatedMiddleware } from "../../middlewares/isAuthenticated.ts";
23+
import {
24+
ANTHROPIC_PROVIDER,
25+
GOOGLE_PROVIDER,
26+
OPENAI_PROVIDER,
27+
} from "../../../manifest/dependencyManifest/labeling/model.ts";
2328

2429
function builder(
2530
yargs: Arguments & {
@@ -1031,6 +1036,61 @@ export async function generateConfig(
10311036
// Show final file selection to the user
10321037
showFinalFileSelection(workDir, includePatterns, excludePatterns);
10331038

1039+
// Labeling configuration
1040+
console.info("\n🏷️ LABELING CONFIGURATION");
1041+
console.info(
1042+
"Labeling helps categorize and organize your code dependencies using AI models.",
1043+
);
1044+
1045+
const enableLabeling = await confirm({
1046+
message: "Would you like to enable AI-powered labeling?",
1047+
default: false,
1048+
});
1049+
1050+
let labelingConfig:
1051+
| z.infer<typeof localConfigSchema>["labeling"]
1052+
| undefined = undefined;
1053+
1054+
if (enableLabeling) {
1055+
console.info("\n🤖 AI MODEL SELECTION");
1056+
console.info(
1057+
"Choose an AI provider for labeling your dependencies:",
1058+
);
1059+
1060+
const modelProvider = await select({
1061+
message: "Select AI model provider:",
1062+
choices: [
1063+
{ name: "OpenAI (GPT-4o-mini)", value: OPENAI_PROVIDER },
1064+
{ name: "Google (Gemini 2.5 Flash)", value: GOOGLE_PROVIDER },
1065+
{ name: "Anthropic (Claude 3.5 Sonnet)", value: ANTHROPIC_PROVIDER },
1066+
],
1067+
}) as
1068+
| typeof OPENAI_PROVIDER
1069+
| typeof GOOGLE_PROVIDER
1070+
| typeof ANTHROPIC_PROVIDER;
1071+
1072+
const maxConcurrency = await input({
1073+
message: "Enter maximum concurrent requests (leave empty for unlimited):",
1074+
validate: (value) => {
1075+
if (!value.trim()) return true; // Allow empty for unlimited
1076+
const num = parseInt(value);
1077+
if (isNaN(num) || num <= 0) {
1078+
return "Please enter a positive number or leave empty for unlimited";
1079+
}
1080+
return true;
1081+
},
1082+
});
1083+
1084+
labelingConfig = {
1085+
modelProvider,
1086+
maxConcurrency: maxConcurrency.trim()
1087+
? parseInt(maxConcurrency)
1088+
: undefined,
1089+
};
1090+
1091+
console.info("✅ Labeling configuration added");
1092+
}
1093+
10341094
// Build the config object
10351095
const config: z.infer<typeof localConfigSchema> = {
10361096
language: language,
@@ -1052,5 +1112,10 @@ export async function generateConfig(
10521112
config.c = cConfig;
10531113
}
10541114

1115+
// Add labeling config if it exists
1116+
if (labelingConfig) {
1117+
config.labeling = labelingConfig;
1118+
}
1119+
10551120
return config;
10561121
}

src/cli/handlers/manifest/generate.ts

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,9 @@ function builder(
4545
}
4646
return value;
4747
},
48+
}).option("labelingApiKey", {
49+
type: "string",
50+
description: "The API key to use for the labeling",
4851
});
4952
}
5053

@@ -128,6 +131,7 @@ async function handler(
128131
branch?: string;
129132
commitSha?: string;
130133
commitShaDate?: string;
134+
labelingApiKey?: string;
131135
},
132136
) {
133137
const napiConfig = argv.napiConfig as z.infer<typeof localConfigSchema>;
@@ -217,7 +221,12 @@ async function handler(
217221

218222
console.info(`📊 Processing ${files.size} files...`);
219223

220-
const dependencyManifest = generateDependencyManifest(files, napiConfig);
224+
const dependencyManifest = await generateDependencyManifest(
225+
files,
226+
napiConfig,
227+
globalConfig,
228+
argv.labelingApiKey,
229+
);
221230

222231
// Upload manifest to API instead of writing to disk
223232
const apiService = new ApiService(

src/cli/handlers/set/apiKey.ts

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
import type { Arguments } from "yargs-types";
2+
import type { z } from "zod";
3+
import {
4+
type globalConfigSchema,
5+
setConfig,
6+
} from "../../middlewares/globalConfig.ts";
7+
import {
8+
ANTHROPIC_PROVIDER,
9+
GOOGLE_PROVIDER,
10+
type ModelProvider,
11+
OPENAI_PROVIDER,
12+
} from "../../../manifest/dependencyManifest/labeling/model.ts";
13+
import { input, select } from "@inquirer/prompts";
14+
15+
async function handler(
16+
argv: Arguments & {
17+
globalConfig: z.infer<typeof globalConfigSchema>;
18+
},
19+
) {
20+
const globalConfig = argv.globalConfig as z.infer<typeof globalConfigSchema>;
21+
22+
const provider = await select({
23+
message: "Select a provider",
24+
choices: [
25+
{ name: "Google", value: GOOGLE_PROVIDER },
26+
{ name: "OpenAI", value: OPENAI_PROVIDER },
27+
{ name: "Anthropic", value: ANTHROPIC_PROVIDER },
28+
],
29+
}) as ModelProvider;
30+
31+
const apiKey = await input({
32+
message: "Enter the API key",
33+
validate: (value) => {
34+
if (value.length === 0) {
35+
return "API key cannot be empty";
36+
}
37+
return true;
38+
},
39+
});
40+
41+
const labeling = globalConfig.labeling || { apiKeys: {} };
42+
labeling.apiKeys[provider] = apiKey;
43+
setConfig({ ...globalConfig, labeling });
44+
45+
console.info("API key set successfully");
46+
}
47+
48+
export default {
49+
command: "apiKey",
50+
describe: "set an API key for a model provider in your global config",
51+
builder: () => {},
52+
handler,
53+
};

src/cli/handlers/set/index.ts

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
import apiKeyHandler from "./apiKey.ts";
2+
import type { Arguments } from "yargs-types";
3+
import type { globalConfigSchema } from "../../middlewares/globalConfig.ts";
4+
import type { z } from "zod";
5+
6+
function builder(
7+
yargs: Arguments & {
8+
globalConfig: z.infer<typeof globalConfigSchema>;
9+
},
10+
) {
11+
return yargs
12+
.command(apiKeyHandler)
13+
.demandCommand(1, "You need to specify a valid command");
14+
}
15+
16+
export default {
17+
command: "set",
18+
describe: "set a value in the global config",
19+
builder,
20+
handler: () => {},
21+
};

src/cli/index.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import {
55
} from "./middlewares/checkVersion.ts";
66
import loginCommand from "./handlers/login/index.ts";
77
import initCommand from "./handlers/init/index.ts";
8+
import setCommand from "./handlers/set/index.ts";
89
import manifestCommand from "./handlers/manifest/index.ts";
910
import extractCommand from "./handlers/extract/index.ts";
1011
import { globalConfigMiddleware } from "./middlewares/globalConfig.ts";
@@ -27,6 +28,7 @@ export function initCli() {
2728
.middleware(globalConfigMiddleware)
2829
.command(loginCommand)
2930
.command(initCommand)
31+
.command(setCommand)
3032
.command(manifestCommand)
3133
.command(extractCommand)
3234
.demandCommand(1, "You need to specify a command")

src/cli/middlewares/globalConfig.ts

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,13 @@ export const globalConfigSchema = z.object({
66
jwt: z.string().optional(),
77
token: z.string().optional(),
88
apiHost: z.string(),
9+
labeling: z.object({
10+
apiKeys: z.object({
11+
google: z.string().optional(),
12+
openai: z.string().optional(),
13+
anthropic: z.string().optional(),
14+
}),
15+
}).optional(),
916
});
1017

1118
export const defaultApiHost = "https://api.nanoapi.io";

src/cli/middlewares/napiConfig.ts

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,11 @@ import {
1010
javaLanguage,
1111
pythonLanguage,
1212
} from "../../helpers/treeSitter/parsers.ts";
13+
import {
14+
ANTHROPIC_PROVIDER,
15+
GOOGLE_PROVIDER,
16+
OPENAI_PROVIDER,
17+
} from "../../manifest/dependencyManifest/labeling/model.ts";
1318

1419
const pythonVersions = Object.keys(pythonStdlibList);
1520

@@ -38,6 +43,14 @@ export const localConfigSchema = z.object({
3843
exclude: z.array(z.string()).optional(),
3944
}),
4045
outDir: z.string(),
46+
labeling: z.object({
47+
modelProvider: z.enum([
48+
GOOGLE_PROVIDER,
49+
OPENAI_PROVIDER,
50+
ANTHROPIC_PROVIDER,
51+
]),
52+
maxConcurrency: z.number().optional(),
53+
}).optional(),
4154
});
4255

4356
const napiConfigFileName = ".napirc";

src/languagePlugins/python/dependencyResolver/index.ts

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -126,6 +126,18 @@ export class PythonDependencyResolver {
126126
const symbolDependencies: SymbolDependency = {
127127
id: symbol.id,
128128
type: symbol.type,
129+
positions: symbol.nodes.map((node) => ({
130+
start: {
131+
index: node.startIndex,
132+
row: node.startPosition.row,
133+
column: node.startPosition.column,
134+
},
135+
end: {
136+
index: node.endIndex,
137+
row: node.endPosition.row,
138+
column: node.endPosition.column,
139+
},
140+
})),
129141
metrics: {
130142
characterCount: complexityMetrics.characterCount,
131143
codeCharacterCount: complexityMetrics.codeCharacterCount,

src/languagePlugins/python/dependencyResolver/types.ts

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,19 @@ export interface SymbolDependency {
2727
id: string;
2828
/** Symbol type (class, function, variable) */
2929
type: PythonSymbolType;
30+
/** Positions of the symbol in the file */
31+
positions: {
32+
start: {
33+
index: number;
34+
row: number;
35+
column: number;
36+
};
37+
end: {
38+
index: number;
39+
row: number;
40+
column: number;
41+
};
42+
}[];
3043
/** Size metrics for the symbol */
3144
metrics: {
3245
/** Total character count in the symbol */

0 commit comments

Comments
 (0)