Skip to content
Open
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
8 changes: 8 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -5,3 +5,11 @@
*.stTheme.cache
*.sublime-workspace
*.sublime-project
logs
*.log
npm-debug.log*
yarn-debug.log*
yarn-error.log*
lerna-debug.log*
node_modules/
dist
11 changes: 11 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -127,6 +127,16 @@ or save the option as a preference using this command:
ai config set SILENT_MODE=true
```

### Thinking mode

For OpenAI-compatible endpoints that support `chat_template_kwargs.enable_thinking`, you can explicitly enable or disable thinking:

```bash
ai config set ENABLE_THINKING=false
```

When unset, AI Shell does not send `chat_template_kwargs`.

### Custom API endpoint

You can custom OpenAI API endpoint to set OPENAI_API_ENDPOINT(default: `https://api.openai.com/v1`)
Expand Down Expand Up @@ -181,6 +191,7 @@ To get an interactive UI like below:
│ ○ OpenAI Key
│ ○ OpenAI API Endpoint
│ ○ Silent Mode
│ ○ Enable Thinking
│ ● Model (gpt-4o-mini)
│ ○ Language
│ ○ Cancel
Expand Down
5 changes: 5 additions & 0 deletions src/commands/chat.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ export default command(
OPENAI_KEY: key,
OPENAI_API_ENDPOINT: apiEndpoint,
MODEL: model,
ENABLE_THINKING: enableThinking,
} = await getConfig();
const chatHistory: ChatCompletionRequestMessage[] = [];

Expand Down Expand Up @@ -51,6 +52,7 @@ export default command(
key,
model,
apiEndpoint,
enableThinking,
});

infoSpin.stop(`${green('AI Shell:')}`);
Expand All @@ -77,19 +79,22 @@ async function getResponse({
key,
model,
apiEndpoint,
enableThinking,
}: {
prompt: string | ChatCompletionRequestMessage[];
number?: number;
model?: string;
key: string;
apiEndpoint: string;
enableThinking?: boolean;
}) {
const stream = await generateCompletion({
prompt,
key,
model,
number,
apiEndpoint,
enableThinking,
});

const iterableStream = streamToIterable(stream);
Expand Down
42 changes: 31 additions & 11 deletions src/helpers/completion.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,11 +34,13 @@ export async function getScriptAndInfo({
key,
model,
apiEndpoint,
enableThinking,
}: {
prompt: string;
key: string;
model?: string;
apiEndpoint: string;
enableThinking?: boolean;
}) {
const fullPrompt = getFullPrompt(prompt);
const stream = await generateCompletion({
Expand All @@ -47,6 +49,7 @@ export async function getScriptAndInfo({
key,
model,
apiEndpoint,
enableThinking,
});
const iterableStream = streamToIterable(stream);
return {
Expand All @@ -61,26 +64,37 @@ export async function generateCompletion({
key,
model,
apiEndpoint,
enableThinking,
}: {
prompt: string | ChatCompletionRequestMessage[];
number?: number;
model?: string;
key: string;
apiEndpoint: string;
enableThinking?: boolean;
}) {
const openAi = getOpenAi(key, apiEndpoint);
try {
const completion = await openAi.createChatCompletion(
{
model: model || 'gpt-4o-mini',
messages: Array.isArray(prompt)
? prompt
: [{ role: 'user', content: prompt }],
n: Math.min(number, 10),
stream: true,
},
{ responseType: 'stream' }
);
const request = {
model: model || 'gpt-4o-mini',
messages: Array.isArray(prompt)
? prompt
: [{ role: 'user', content: prompt }],
n: Math.min(number, 10),
stream: true,
};

if (enableThinking !== undefined) {
Object.assign(request, {
chat_template_kwargs: {
enable_thinking: enableThinking,
},
});
}

const completion = await openAi.createChatCompletion(request as any, {
responseType: 'stream',
});

return completion.data as unknown as IncomingMessage;
} catch (err) {
Expand Down Expand Up @@ -141,11 +155,13 @@ export async function getExplanation({
key,
model,
apiEndpoint,
enableThinking,
}: {
script: string;
key: string;
model?: string;
apiEndpoint: string;
enableThinking?: boolean;
}) {
const prompt = getExplanationPrompt(script);
const stream = await generateCompletion({
Expand All @@ -154,6 +170,7 @@ export async function getExplanation({
number: 1,
model,
apiEndpoint,
enableThinking,
});
const iterableStream = streamToIterable(stream);
return { readExplanation: readData(iterableStream) };
Expand All @@ -165,12 +182,14 @@ export async function getRevision({
key,
model,
apiEndpoint,
enableThinking,
}: {
prompt: string;
code: string;
key: string;
model?: string;
apiEndpoint: string;
enableThinking?: boolean;
}) {
const fullPrompt = getRevisionPrompt(prompt, code);
const stream = await generateCompletion({
Expand All @@ -179,6 +198,7 @@ export async function getRevision({
number: 1,
model,
apiEndpoint,
enableThinking,
});
const iterableStream = streamToIterable(stream);
return {
Expand Down
23 changes: 23 additions & 0 deletions src/helpers/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,13 @@ const configParsers = {
SILENT_MODE(mode?: string) {
return String(mode).toLowerCase() === 'true';
},
ENABLE_THINKING(mode?: string) {
if (mode === undefined || mode === '') {
return undefined;
}

return String(mode).toLowerCase() === 'true';
},
OPENAI_API_ENDPOINT(apiEndpoint?: string) {
return apiEndpoint || 'https://api.openai.com/v1';
},
Expand Down Expand Up @@ -142,6 +149,14 @@ export const showConfigUI = async () => {
? config.SILENT_MODE.toString()
: i18n.t('(not set)'),
},
{
label: i18n.t('Enable Thinking'),
value: 'ENABLE_THINKING',
hint:
config.ENABLE_THINKING === undefined
? i18n.t('(not set)')
: config.ENABLE_THINKING.toString(),
},
{
label: i18n.t('Model'),
value: 'MODEL',
Expand Down Expand Up @@ -187,6 +202,14 @@ export const showConfigUI = async () => {
});
if (p.isCancel(silentMode)) return;
await setConfigs([['SILENT_MODE', silentMode ? 'true' : 'false']]);
} else if (choice === 'ENABLE_THINKING') {
const enableThinking = await p.confirm({
message: i18n.t('Enable thinking?'),
});
if (p.isCancel(enableThinking)) return;
await setConfigs([
['ENABLE_THINKING', enableThinking ? 'true' : 'false'],
]);
} else if (choice === 'MODEL') {
const { OPENAI_KEY: key, OPENAI_API_ENDPOINT: apiEndpoint } =
await getConfig();
Expand Down
38 changes: 33 additions & 5 deletions src/prompt.ts
Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +106,7 @@ export async function prompt({
SILENT_MODE,
OPENAI_API_ENDPOINT: apiEndpoint,
MODEL: model,
ENABLE_THINKING: enableThinking,
} = await getConfig();
const skipCommandExplanation = silentMode || SILENT_MODE;

Expand All @@ -120,6 +121,7 @@ export async function prompt({
key,
model,
apiEndpoint,
enableThinking,
});
spin.stop(`${i18n.t('Your script')}:`);
console.log('');
Expand All @@ -136,6 +138,7 @@ export async function prompt({
key,
model,
apiEndpoint,
enableThinking,
});
spin.stop(`${i18n.t('Explanation')}:`);
console.log('');
Expand All @@ -146,15 +149,23 @@ export async function prompt({
}
}

await runOrReviseFlow(script, key, model, apiEndpoint, silentMode);
await runOrReviseFlow(
script,
key,
model,
apiEndpoint,
silentMode,
enableThinking
);
}

async function runOrReviseFlow(
script: string,
key: string,
model: string,
apiEndpoint: string,
silentMode?: boolean
silentMode?: boolean,
enableThinking?: boolean
) {
const emptyScript = script.trim() === '';

Expand Down Expand Up @@ -191,7 +202,14 @@ async function runOrReviseFlow(
label: '🔁 ' + i18n.t('Revise'),
hint: i18n.t('Give feedback via prompt and get a new result'),
value: async () => {
await revisionFlow(script, key, model, apiEndpoint, silentMode);
await revisionFlow(
script,
key,
model,
apiEndpoint,
silentMode,
enableThinking
);
},
},
{
Expand Down Expand Up @@ -223,7 +241,8 @@ async function revisionFlow(
key: string,
model: string,
apiEndpoint: string,
silentMode?: boolean
silentMode?: boolean,
enableThinking?: boolean
) {
const revision = await promptForRevision();
const spin = p.spinner();
Expand All @@ -234,6 +253,7 @@ async function revisionFlow(
key,
model,
apiEndpoint,
enableThinking,
});
spin.stop(`${i18n.t(`Your new script`)}:`);

Expand All @@ -251,6 +271,7 @@ async function revisionFlow(
key,
model,
apiEndpoint,
enableThinking,
});

infoSpin.stop(`${i18n.t('Explanation')}:`);
Expand All @@ -261,7 +282,14 @@ async function revisionFlow(
console.log(dim('•'));
}

await runOrReviseFlow(script, key, model, apiEndpoint, silentMode);
await runOrReviseFlow(
script,
key,
model,
apiEndpoint,
silentMode,
enableThinking
);
}

export const parseAssert = (name: string, condition: any, message: string) => {
Expand Down