Skip to content

Conversation

@tiye
Copy link
Member

@tiye tiye commented Jan 26, 2026

No description provided.

@tiye tiye requested review from a team and Copilot January 26, 2026 11:43
Copy link

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

This PR migrates the codebase from using Google's GenAI chat/stream API to the newer interactions API. The changes introduce CLI argument parsing for better configuration, update dependencies significantly, and restructure how the agent communicates with the AI model.

Changes:

  • Migrated from chat API to interactions API with stateful conversation handling via interaction IDs
  • Added CLI argument parsing for model selection, base URL, verbose mode, and thinking budget
  • Updated @google/genai from v1.10.0 to v1.38.0 and other dependencies to latest versions
  • Removed index.html file (web interface) and updated CI to use Node 24

Reviewed changes

Copilot reviewed 5 out of 6 changed files in this pull request and generated 14 comments.

Show a summary per file
File Description
yarn.lock Updated all dependencies; registry changed to npmmirror.com (Chinese mirror)
src/main.mts Complete API migration with new request/response handling, CLI args, schema normalization, and experimental warning suppression
package.json Updated @google/genai to 1.38.0, chalk to 5.6.2, string-width to 8.1.0, and moved vite to devDependencies
index.html Deleted (web interface removed)
.github/workflows/upload.yaml Updated Node version from 22 to 24
.gitattributes Removed calcit.cirru linguist configuration

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

const macrophyllaModel =
(args.model as string) ??
process.env["MACROPHYLLA_MODEL"] ??
"gemini-3-flash-preview";
Copy link

Copilot AI Jan 26, 2026

Choose a reason for hiding this comment

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

The model name "gemini-3-flash-preview" appears to be non-standard. Based on Google's typical naming conventions, this should likely be "gemini-2.0-flash-exp" or similar. Please verify this model name exists and is correct, as using a non-existent model will cause runtime errors.

Suggested change
"gemini-3-flash-preview";
"gemini-2.0-flash";

Copilot uses AI. Check for mistakes.
Comment on lines +80 to +81
baseUrl: geminiBaseUrl,
} as any);
Copy link

Copilot AI Jan 26, 2026

Choose a reason for hiding this comment

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

Using as any to bypass type checking when creating the GoogleGenAI instance indicates the baseUrl parameter may not be typed correctly in the library. This could hide type errors and lead to runtime failures if the API structure doesn't match expectations. Consider verifying the correct way to pass baseUrl in version 1.38.0 of @google/genai, or file an issue with the library maintainers if this is a typing limitation.

Suggested change
baseUrl: geminiBaseUrl,
} as any);
...(geminiBaseUrl ? { baseUrl: geminiBaseUrl } : {}),
});

Copilot uses AI. Check for mistakes.
Comment on lines +106 to +132
if (!schema) return schema;
const newSchema = { ...schema };
if (typeof newSchema.type === "number") {
const types = [
"unspecified",
"string",
"number",
"integer",
"boolean",
"array",
"object",
];
newSchema.type = types[newSchema.type] || newSchema.type;
}
if (typeof newSchema.type === "string") {
newSchema.type = newSchema.type.toLowerCase();
}
if (newSchema.properties) {
const newProps: any = {};
for (const key in newSchema.properties) {
newProps[key] = normalizeSchema(newSchema.properties[key]);
}
newSchema.properties = newProps;
}
if (newSchema.items) {
newSchema.items = normalizeSchema(newSchema.items);
}
Copy link

Copilot AI Jan 26, 2026

Choose a reason for hiding this comment

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

The normalizeSchema function handles type conversion but doesn't validate that the input schema is well-formed. Consider adding validation to check for unexpected schema structures or malformed data, which could cause runtime errors when the schema is used by the API.

Suggested change
if (!schema) return schema;
const newSchema = { ...schema };
if (typeof newSchema.type === "number") {
const types = [
"unspecified",
"string",
"number",
"integer",
"boolean",
"array",
"object",
];
newSchema.type = types[newSchema.type] || newSchema.type;
}
if (typeof newSchema.type === "string") {
newSchema.type = newSchema.type.toLowerCase();
}
if (newSchema.properties) {
const newProps: any = {};
for (const key in newSchema.properties) {
newProps[key] = normalizeSchema(newSchema.properties[key]);
}
newSchema.properties = newProps;
}
if (newSchema.items) {
newSchema.items = normalizeSchema(newSchema.items);
}
// Basic structural validation: schema must be a non-null object.
if (schema === null || typeof schema !== "object") {
throw new TypeError(
`normalizeSchema expected a non-null object, received ${typeof schema}`,
);
}
const newSchema: any = { ...schema };
// Normalize and validate the "type" field if present.
const allowedTypes = [
"unspecified",
"string",
"number",
"integer",
"boolean",
"array",
"object",
];
if (
newSchema.type !== undefined &&
typeof newSchema.type !== "number" &&
typeof newSchema.type !== "string"
) {
throw new TypeError(
`schema.type must be a string or number when present; received ${typeof newSchema.type}`,
);
}
if (typeof newSchema.type === "number") {
newSchema.type = allowedTypes[newSchema.type] || newSchema.type;
}
if (typeof newSchema.type === "string") {
newSchema.type = newSchema.type.toLowerCase();
if (!allowedTypes.includes(newSchema.type)) {
throw new Error(`Unsupported schema type: ${newSchema.type}`);
}
}
// Validate and normalize nested "properties" if present.
if (newSchema.properties !== undefined) {
if (
newSchema.properties === null ||
typeof newSchema.properties !== "object" ||
Array.isArray(newSchema.properties)
) {
throw new TypeError("schema.properties must be a non-null object map");
}
const newProps: any = {};
for (const key in newSchema.properties) {
if (Object.prototype.hasOwnProperty.call(newSchema.properties, key)) {
newProps[key] = normalizeSchema(newSchema.properties[key]);
}
}
newSchema.properties = newProps;
}
// Validate and normalize "items" for array schemas if present.
if (newSchema.items !== undefined) {
if (newSchema.items === null || typeof newSchema.items !== "object") {
throw new TypeError("schema.items must be a non-null object");
}
newSchema.items = normalizeSchema(newSchema.items);
}

Copilot uses AI. Check for mistakes.
Comment on lines +172 to +176
} else if (state.history.length > 0) {
// If we have history but no lastInteractionId, use stateless mode by sending full history
requestPayload.input = state.history.concat([
{ role: "user", content: state.currentMessageParts },
]);
Copy link

Copilot AI Jan 26, 2026

Choose a reason for hiding this comment

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

The fallback logic in lines 172-177 attempts to send full history when there's no lastInteractionId, but this reassigns requestPayload.input which would overwrite the current user message. The concat creates a new array but the structure may not match what the API expects - verify that the interactions API supports this fallback pattern and that the message format is correct.

Suggested change
} else if (state.history.length > 0) {
// If we have history but no lastInteractionId, use stateless mode by sending full history
requestPayload.input = state.history.concat([
{ role: "user", content: state.currentMessageParts },
]);

Copilot uses AI. Check for mistakes.
Comment on lines +46 to +47
history: any[];
currentMessageParts: any[];
Copy link

Copilot AI Jan 26, 2026

Choose a reason for hiding this comment

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

Extensive use of any types throughout the code (lines 46-47, 81, 105, 124, 141-142, 150, 161, 179, 181-182, 228-230, 323) significantly weakens type safety. While this may be necessary during API migration, consider creating proper TypeScript interfaces or types for the interactions API structures (e.g., InteractionRequest, InteractionResponse, InteractionChunk) to maintain type safety and catch potential runtime errors at compile time.

Suggested change
history: any[];
currentMessageParts: any[];
history: Content[];
currentMessageParts: Part[];

Copilot uses AI. Check for mistakes.
Comment on lines +14 to +25
// --- Suppress Experimental Warning ---
const originalWarn = console.warn;
console.warn = (...args: any[]) => {
if (
typeof args[0] === "string" &&
args[0].includes("Interactions usage is experimental")
) {
return;
}
originalWarn(...args);
};

Copy link

Copilot AI Jan 26, 2026

Choose a reason for hiding this comment

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

The code suppresses experimental warnings by overriding console.warn, which is a risky approach that could hide legitimate warnings. Consider using more targeted warning suppression or documenting why this experimental API is acceptable for production use. Additionally, this suppression occurs before all imports, which means it might miss warnings from import-time code execution.

Suggested change
// --- Suppress Experimental Warning ---
const originalWarn = console.warn;
console.warn = (...args: any[]) => {
if (
typeof args[0] === "string" &&
args[0].includes("Interactions usage is experimental")
) {
return;
}
originalWarn(...args);
};

Copilot uses AI. Check for mistakes.
Comment on lines +205 to +210
if (
output.type === "function_call" &&
!newToolCalls.some((c) => c.id === output.id)
) {
newToolCalls.push(output);
}
Copy link

Copilot AI Jan 26, 2026

Choose a reason for hiding this comment

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

The code checks for duplicate function calls by comparing IDs (!newToolCalls.some((c) => c.id === output.id)), but if function calls from content.delta events don't have IDs or have different ID structures than those in interaction.complete, this could lead to duplicate or missing tool calls. Verify the ID structure is consistent across both event types.

Copilot uses AI. Check for mistakes.
Comment on lines 220 to 224
return {
modelResponseParts,
toolCalls: newToolCalls.length > 0 ? newToolCalls : undefined,
interactionId,
};
Copy link

Copilot AI Jan 26, 2026

Choose a reason for hiding this comment

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

If the stream completes without an interaction.complete event, the interactionId will be an empty string, which may cause issues when used as previous_interaction_id in subsequent requests. Consider handling this case by either throwing an error, logging a warning, or falling back to stateless mode.

Copilot uses AI. Check for mistakes.
]);
}

const response = await (genAi as any).interactions.create(requestPayload);
Copy link

Copilot AI Jan 26, 2026

Choose a reason for hiding this comment

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

The code uses (genAi as any).interactions.create(requestPayload) which bypasses type checking. This indicates the interactions API is not officially typed in the @google/genai library. Consider checking if there's official documentation or types for this API, and document the expected structure of requestPayload and response for future maintainability.

Copilot uses AI. Check for mistakes.
- uses: actions/setup-node@v4
with:
node-version: 22
node-version: 24
Copy link

Copilot AI Jan 26, 2026

Choose a reason for hiding this comment

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

The Node.js version was updated from 22 to 24 in the CI workflow. Node.js 24 is not yet released (as of January 2025, the latest LTS is Node 22). This appears to be a future-proofing change or potentially an error. Verify that Node 24 is available and stable, or consider using Node 22 or 23 (current versions) instead.

Suggested change
node-version: 24
node-version: 22

Copilot uses AI. Check for mistakes.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants