mirror of
https://github.com/AutoMaker-Org/automaker.git
synced 2026-01-30 14:22:02 +00:00
feat(auto-mode): skip memory extraction when Claude not configured and add reasoning effort support
- Skip learning extraction when ANTHROPIC_API_KEY is not available - Add reasoningEffort parameter to simpleQuery for Codex model configuration - Add stdinData support to spawnProcess for CLI stdin input - Update UI API types for model override with reasoning support
This commit is contained in:
@@ -15,7 +15,13 @@
|
||||
*/
|
||||
|
||||
import { ProviderFactory } from './provider-factory.js';
|
||||
import type { ProviderMessage, ContentBlock, ThinkingLevel } from '@automaker/types';
|
||||
import type {
|
||||
ProviderMessage,
|
||||
ContentBlock,
|
||||
ThinkingLevel,
|
||||
ReasoningEffort,
|
||||
} from '@automaker/types';
|
||||
import { stripProviderPrefix } from '@automaker/types';
|
||||
|
||||
/**
|
||||
* Options for simple query execution
|
||||
@@ -42,6 +48,8 @@ export interface SimpleQueryOptions {
|
||||
};
|
||||
/** Thinking level for Claude models */
|
||||
thinkingLevel?: ThinkingLevel;
|
||||
/** Reasoning effort for Codex/OpenAI models */
|
||||
reasoningEffort?: ReasoningEffort;
|
||||
/** If true, runs in read-only mode (no file writes) */
|
||||
readOnly?: boolean;
|
||||
/** Setting sources for CLAUDE.md loading */
|
||||
@@ -97,6 +105,7 @@ const DEFAULT_MODEL = 'claude-sonnet-4-20250514';
|
||||
export async function simpleQuery(options: SimpleQueryOptions): Promise<SimpleQueryResult> {
|
||||
const model = options.model || DEFAULT_MODEL;
|
||||
const provider = ProviderFactory.getProviderForModel(model);
|
||||
const bareModel = stripProviderPrefix(model);
|
||||
|
||||
let responseText = '';
|
||||
let structuredOutput: Record<string, unknown> | undefined;
|
||||
@@ -104,7 +113,8 @@ export async function simpleQuery(options: SimpleQueryOptions): Promise<SimpleQu
|
||||
// Build provider options
|
||||
const providerOptions = {
|
||||
prompt: options.prompt,
|
||||
model: model,
|
||||
model: bareModel,
|
||||
originalModel: model,
|
||||
cwd: options.cwd,
|
||||
systemPrompt: options.systemPrompt,
|
||||
maxTurns: options.maxTurns ?? 1,
|
||||
@@ -112,6 +122,7 @@ export async function simpleQuery(options: SimpleQueryOptions): Promise<SimpleQu
|
||||
abortController: options.abortController,
|
||||
outputFormat: options.outputFormat,
|
||||
thinkingLevel: options.thinkingLevel,
|
||||
reasoningEffort: options.reasoningEffort,
|
||||
readOnly: options.readOnly,
|
||||
settingSources: options.settingSources,
|
||||
};
|
||||
@@ -176,6 +187,7 @@ export async function simpleQuery(options: SimpleQueryOptions): Promise<SimpleQu
|
||||
export async function streamingQuery(options: StreamingQueryOptions): Promise<SimpleQueryResult> {
|
||||
const model = options.model || DEFAULT_MODEL;
|
||||
const provider = ProviderFactory.getProviderForModel(model);
|
||||
const bareModel = stripProviderPrefix(model);
|
||||
|
||||
let responseText = '';
|
||||
let structuredOutput: Record<string, unknown> | undefined;
|
||||
@@ -183,7 +195,8 @@ export async function streamingQuery(options: StreamingQueryOptions): Promise<Si
|
||||
// Build provider options
|
||||
const providerOptions = {
|
||||
prompt: options.prompt,
|
||||
model: model,
|
||||
model: bareModel,
|
||||
originalModel: model,
|
||||
cwd: options.cwd,
|
||||
systemPrompt: options.systemPrompt,
|
||||
maxTurns: options.maxTurns ?? 250,
|
||||
@@ -191,6 +204,7 @@ export async function streamingQuery(options: StreamingQueryOptions): Promise<Si
|
||||
abortController: options.abortController,
|
||||
outputFormat: options.outputFormat,
|
||||
thinkingLevel: options.thinkingLevel,
|
||||
reasoningEffort: options.reasoningEffort,
|
||||
readOnly: options.readOnly,
|
||||
settingSources: options.settingSources,
|
||||
};
|
||||
|
||||
@@ -21,7 +21,7 @@ import type {
|
||||
ThinkingLevel,
|
||||
PlanningMode,
|
||||
} from '@automaker/types';
|
||||
import { DEFAULT_PHASE_MODELS, stripProviderPrefix } from '@automaker/types';
|
||||
import { DEFAULT_PHASE_MODELS, isClaudeModel, stripProviderPrefix } from '@automaker/types';
|
||||
import {
|
||||
buildPromptWithImages,
|
||||
classifyError,
|
||||
@@ -3586,10 +3586,29 @@ If nothing notable: {"learnings": []}`;
|
||||
const phaseModelEntry =
|
||||
settings?.phaseModels?.memoryExtractionModel || DEFAULT_PHASE_MODELS.memoryExtractionModel;
|
||||
const { model } = resolvePhaseModel(phaseModelEntry);
|
||||
const hasClaudeKey = Boolean(process.env.ANTHROPIC_API_KEY);
|
||||
let resolvedModel = model;
|
||||
|
||||
if (isClaudeModel(model) && !hasClaudeKey) {
|
||||
const fallbackModel = feature.model
|
||||
? resolveModelString(feature.model, DEFAULT_MODELS.claude)
|
||||
: null;
|
||||
if (fallbackModel && !isClaudeModel(fallbackModel)) {
|
||||
console.log(
|
||||
`[AutoMode] Claude not configured for memory extraction; using feature model "${fallbackModel}".`
|
||||
);
|
||||
resolvedModel = fallbackModel;
|
||||
} else {
|
||||
console.log(
|
||||
'[AutoMode] Claude not configured for memory extraction; skipping learning extraction.'
|
||||
);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
const result = await simpleQuery({
|
||||
prompt: userPrompt,
|
||||
model,
|
||||
model: resolvedModel,
|
||||
cwd: projectPath,
|
||||
maxTurns: 1,
|
||||
allowedTools: [],
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { useState, useCallback, useMemo } from 'react';
|
||||
import { useAppStore } from '@/store/app-store';
|
||||
import type { ModelAlias, CursorModelId, PhaseModelKey, PhaseModelEntry } from '@automaker/types';
|
||||
import type { ModelId, PhaseModelKey, PhaseModelEntry } from '@automaker/types';
|
||||
import { DEFAULT_PHASE_MODELS } from '@automaker/types';
|
||||
|
||||
export interface UseModelOverrideOptions {
|
||||
@@ -14,7 +14,7 @@ export interface UseModelOverrideResult {
|
||||
/** The effective model entry (override or global default) */
|
||||
effectiveModelEntry: PhaseModelEntry;
|
||||
/** The effective model string (for backward compatibility with APIs that only accept strings) */
|
||||
effectiveModel: ModelAlias | CursorModelId;
|
||||
effectiveModel: ModelId;
|
||||
/** Whether the model is currently overridden */
|
||||
isOverridden: boolean;
|
||||
/** Set a model override */
|
||||
@@ -32,7 +32,7 @@ export interface UseModelOverrideResult {
|
||||
*/
|
||||
function normalizeEntry(entry: PhaseModelEntry | string): PhaseModelEntry {
|
||||
if (typeof entry === 'string') {
|
||||
return { model: entry as ModelAlias | CursorModelId };
|
||||
return { model: entry as ModelId };
|
||||
}
|
||||
return entry;
|
||||
}
|
||||
@@ -40,9 +40,9 @@ function normalizeEntry(entry: PhaseModelEntry | string): PhaseModelEntry {
|
||||
/**
|
||||
* Extract model string from PhaseModelEntry or string
|
||||
*/
|
||||
function extractModel(entry: PhaseModelEntry | string): ModelAlias | CursorModelId {
|
||||
function extractModel(entry: PhaseModelEntry | string): ModelId {
|
||||
if (typeof entry === 'string') {
|
||||
return entry as ModelAlias | CursorModelId;
|
||||
return entry as ModelId;
|
||||
}
|
||||
return entry.model;
|
||||
}
|
||||
|
||||
@@ -10,7 +10,9 @@ import type {
|
||||
IssueValidationResponse,
|
||||
IssueValidationEvent,
|
||||
StoredValidation,
|
||||
AgentModel,
|
||||
ModelId,
|
||||
ThinkingLevel,
|
||||
ReasoningEffort,
|
||||
GitHubComment,
|
||||
IssueCommentsResult,
|
||||
Idea,
|
||||
@@ -314,7 +316,9 @@ export interface GitHubAPI {
|
||||
validateIssue: (
|
||||
projectPath: string,
|
||||
issue: IssueValidationInput,
|
||||
model?: AgentModel
|
||||
model?: ModelId,
|
||||
thinkingLevel?: ThinkingLevel,
|
||||
reasoningEffort?: ReasoningEffort
|
||||
) => Promise<{ success: boolean; message?: string; issueNumber?: number; error?: string }>;
|
||||
/** Check validation status for an issue or all issues */
|
||||
getValidationStatus: (
|
||||
@@ -1294,6 +1298,7 @@ interface SetupAPI {
|
||||
success: boolean;
|
||||
hasAnthropicKey: boolean;
|
||||
hasGoogleKey: boolean;
|
||||
hasOpenaiKey: boolean;
|
||||
}>;
|
||||
deleteApiKey: (
|
||||
provider: string
|
||||
@@ -1377,6 +1382,7 @@ function createMockSetupAPI(): SetupAPI {
|
||||
success: true,
|
||||
hasAnthropicKey: false,
|
||||
hasGoogleKey: false,
|
||||
hasOpenaiKey: false,
|
||||
};
|
||||
},
|
||||
|
||||
@@ -3008,8 +3014,20 @@ function createMockGitHubAPI(): GitHubAPI {
|
||||
mergedPRs: [],
|
||||
};
|
||||
},
|
||||
validateIssue: async (projectPath: string, issue: IssueValidationInput, model?: AgentModel) => {
|
||||
console.log('[Mock] Starting async validation:', { projectPath, issue, model });
|
||||
validateIssue: async (
|
||||
projectPath: string,
|
||||
issue: IssueValidationInput,
|
||||
model?: ModelId,
|
||||
thinkingLevel?: ThinkingLevel,
|
||||
reasoningEffort?: ReasoningEffort
|
||||
) => {
|
||||
console.log('[Mock] Starting async validation:', {
|
||||
projectPath,
|
||||
issue,
|
||||
model,
|
||||
thinkingLevel,
|
||||
reasoningEffort,
|
||||
});
|
||||
|
||||
// Simulate async validation in background
|
||||
setTimeout(() => {
|
||||
|
||||
@@ -36,6 +36,7 @@ import type {
|
||||
import type { Message, SessionListItem } from '@/types/electron';
|
||||
import type { Feature, ClaudeUsageResponse, CodexUsageResponse } from '@/store/app-store';
|
||||
import type { WorktreeAPI, GitAPI, ModelDefinition, ProviderStatus } from '@/types/electron';
|
||||
import type { ModelId, ThinkingLevel, ReasoningEffort } from '@automaker/types';
|
||||
import { getGlobalFileBrowser } from '@/contexts/file-browser-context';
|
||||
|
||||
const logger = createLogger('HttpClient');
|
||||
@@ -1173,6 +1174,7 @@ export class HttpApiClient implements ElectronAPI {
|
||||
success: boolean;
|
||||
hasAnthropicKey: boolean;
|
||||
hasGoogleKey: boolean;
|
||||
hasOpenaiKey: boolean;
|
||||
}> => this.get('/api/setup/api-keys'),
|
||||
|
||||
getPlatform: (): Promise<{
|
||||
@@ -1838,9 +1840,17 @@ export class HttpApiClient implements ElectronAPI {
|
||||
validateIssue: (
|
||||
projectPath: string,
|
||||
issue: IssueValidationInput,
|
||||
model?: string,
|
||||
thinkingLevel?: string
|
||||
) => this.post('/api/github/validate-issue', { projectPath, ...issue, model, thinkingLevel }),
|
||||
model?: ModelId,
|
||||
thinkingLevel?: ThinkingLevel,
|
||||
reasoningEffort?: ReasoningEffort
|
||||
) =>
|
||||
this.post('/api/github/validate-issue', {
|
||||
projectPath,
|
||||
...issue,
|
||||
model,
|
||||
thinkingLevel,
|
||||
reasoningEffort,
|
||||
}),
|
||||
getValidationStatus: (projectPath: string, issueNumber?: number) =>
|
||||
this.post('/api/github/validation-status', { projectPath, issueNumber }),
|
||||
stopValidation: (projectPath: string, issueNumber: number) =>
|
||||
|
||||
@@ -190,7 +190,7 @@ export async function* spawnJSONLProcess(options: SubprocessOptions): AsyncGener
|
||||
* Spawns a subprocess and collects all output
|
||||
*/
|
||||
export async function spawnProcess(options: SubprocessOptions): Promise<SubprocessResult> {
|
||||
const { command, args, cwd, env, abortController } = options;
|
||||
const { command, args, cwd, env, abortController, stdinData } = options;
|
||||
|
||||
const processEnv = {
|
||||
...process.env,
|
||||
@@ -204,10 +204,15 @@ export async function spawnProcess(options: SubprocessOptions): Promise<Subproce
|
||||
const childProcess = spawn(command, args, {
|
||||
cwd,
|
||||
env: processEnv,
|
||||
stdio: ['ignore', 'pipe', 'pipe'],
|
||||
stdio: [stdinData ? 'pipe' : 'ignore', 'pipe', 'pipe'],
|
||||
shell: needsShell,
|
||||
});
|
||||
|
||||
if (stdinData && childProcess.stdin) {
|
||||
childProcess.stdin.write(stdinData);
|
||||
childProcess.stdin.end();
|
||||
}
|
||||
|
||||
let stdout = '';
|
||||
let stderr = '';
|
||||
|
||||
|
||||
Reference in New Issue
Block a user