whspr 1.0.6 → 1.0.10
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.d.ts +2 -0
- package/dist/index.js +46 -5
- package/dist/postprocess.d.ts +3 -0
- package/dist/postprocess.js +10 -8
- package/dist/recorder.js +1 -1
- package/dist/utils/providers.d.ts +4 -0
- package/dist/utils/providers.js +14 -0
- package/package.json +4 -3
package/dist/index.d.ts
CHANGED
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
export declare const DEFAULTS: {
|
|
3
3
|
transcriptionModel: "whisper-large-v3-turbo";
|
|
4
4
|
language: string;
|
|
5
|
+
model: "groq:openai/gpt-oss-120b";
|
|
5
6
|
systemPrompt: string;
|
|
6
7
|
customPromptPrefix: string;
|
|
7
8
|
transcriptionPrefix: string;
|
|
@@ -11,6 +12,7 @@ export interface WhsprSettings {
|
|
|
11
12
|
suffix?: string;
|
|
12
13
|
transcriptionModel?: "whisper-large-v3" | "whisper-large-v3-turbo";
|
|
13
14
|
language?: string;
|
|
15
|
+
model?: string;
|
|
14
16
|
systemPrompt?: string;
|
|
15
17
|
customPromptPrefix?: string;
|
|
16
18
|
transcriptionPrefix?: string;
|
package/dist/index.js
CHANGED
|
@@ -11,18 +11,46 @@ import os from "os";
|
|
|
11
11
|
export const DEFAULTS = {
|
|
12
12
|
transcriptionModel: "whisper-large-v3-turbo",
|
|
13
13
|
language: "en",
|
|
14
|
-
|
|
14
|
+
model: "groq:openai/gpt-oss-120b",
|
|
15
|
+
systemPrompt: `Your task is to fix spelling errors and proper names in transcribed text.
|
|
16
|
+
IMPORTANT: Only correct spelling mistakes and proper nouns (names, places, technical terms).
|
|
17
|
+
Do NOT change wording, phrasing, or sentence structure.
|
|
18
|
+
Do NOT rephrase or rewrite any part of the transcription.
|
|
19
|
+
Preserve the original voice and speaking style exactly as transcribed.`,
|
|
15
20
|
customPromptPrefix: "Here's my custom user prompt:",
|
|
16
21
|
transcriptionPrefix: "Here's my raw transcription output that I need you to edit:",
|
|
17
22
|
};
|
|
23
|
+
// Default settings that will be written to settings.json
|
|
24
|
+
const DEFAULT_SETTINGS = {
|
|
25
|
+
model: DEFAULTS.model,
|
|
26
|
+
};
|
|
18
27
|
const WHSPR_DIR = path.join(os.homedir(), ".whspr");
|
|
19
28
|
const SETTINGS_PATH = path.join(WHSPR_DIR, "settings.json");
|
|
29
|
+
function parseModelProvider(model) {
|
|
30
|
+
const colonIndex = model.indexOf(":");
|
|
31
|
+
if (colonIndex === -1) {
|
|
32
|
+
throw new Error(`Invalid model format: "${model}". Expected "provider:model-name" (e.g., "groq:openai/gpt-oss-120b")`);
|
|
33
|
+
}
|
|
34
|
+
const provider = model.slice(0, colonIndex);
|
|
35
|
+
const modelName = model.slice(colonIndex + 1);
|
|
36
|
+
if (provider !== "groq" && provider !== "anthropic") {
|
|
37
|
+
throw new Error(`Unknown provider: "${provider}". Supported providers: groq, anthropic`);
|
|
38
|
+
}
|
|
39
|
+
return { provider, modelName };
|
|
40
|
+
}
|
|
20
41
|
function loadSettings() {
|
|
21
42
|
try {
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
43
|
+
// Ensure ~/.whspr/ directory exists
|
|
44
|
+
if (!fs.existsSync(WHSPR_DIR)) {
|
|
45
|
+
fs.mkdirSync(WHSPR_DIR, { recursive: true });
|
|
25
46
|
}
|
|
47
|
+
// Create settings.json with defaults if it doesn't exist
|
|
48
|
+
if (!fs.existsSync(SETTINGS_PATH)) {
|
|
49
|
+
fs.writeFileSync(SETTINGS_PATH, JSON.stringify(DEFAULT_SETTINGS, null, 2) + "\n", "utf-8");
|
|
50
|
+
return { ...DEFAULT_SETTINGS };
|
|
51
|
+
}
|
|
52
|
+
const content = fs.readFileSync(SETTINGS_PATH, "utf-8");
|
|
53
|
+
return JSON.parse(content);
|
|
26
54
|
}
|
|
27
55
|
catch (error) {
|
|
28
56
|
// Silently ignore invalid settings file
|
|
@@ -85,13 +113,24 @@ function formatDuration(seconds) {
|
|
|
85
113
|
return `${secs}s`;
|
|
86
114
|
}
|
|
87
115
|
async function main() {
|
|
88
|
-
//
|
|
116
|
+
// Parse model configuration
|
|
117
|
+
const modelConfig = settings.model ?? DEFAULTS.model;
|
|
118
|
+
const { provider, modelName } = parseModelProvider(modelConfig);
|
|
119
|
+
// Check for required API keys before recording
|
|
120
|
+
// Always need GROQ_API_KEY for Whisper transcription
|
|
89
121
|
if (!process.env.GROQ_API_KEY) {
|
|
90
122
|
console.error(chalk.red("Error: GROQ_API_KEY environment variable is not set"));
|
|
91
123
|
console.log(chalk.gray("Get your API key at https://console.groq.com/keys"));
|
|
92
124
|
console.log(chalk.gray("Then run: export GROQ_API_KEY=\"your-api-key\""));
|
|
93
125
|
process.exit(1);
|
|
94
126
|
}
|
|
127
|
+
// Check for provider-specific API key for post-processing
|
|
128
|
+
if (provider === "anthropic" && !process.env.ANTHROPIC_API_KEY) {
|
|
129
|
+
console.error(chalk.red("Error: ANTHROPIC_API_KEY environment variable is not set"));
|
|
130
|
+
console.log(chalk.gray("Get your API key at https://console.anthropic.com/settings/keys"));
|
|
131
|
+
console.log(chalk.gray("Then run: export ANTHROPIC_API_KEY=\"your-api-key\""));
|
|
132
|
+
process.exit(1);
|
|
133
|
+
}
|
|
95
134
|
try {
|
|
96
135
|
// 1. Record audio
|
|
97
136
|
const recording = await record(verbose);
|
|
@@ -115,6 +154,8 @@ async function main() {
|
|
|
115
154
|
// 5. Post-process
|
|
116
155
|
status("Post-processing...");
|
|
117
156
|
let fixedText = await postprocess(rawText, customPrompt, {
|
|
157
|
+
provider,
|
|
158
|
+
modelName,
|
|
118
159
|
systemPrompt: settings.systemPrompt ?? DEFAULTS.systemPrompt,
|
|
119
160
|
customPromptPrefix: settings.customPromptPrefix ?? DEFAULTS.customPromptPrefix,
|
|
120
161
|
transcriptionPrefix: settings.transcriptionPrefix ?? DEFAULTS.transcriptionPrefix,
|
package/dist/postprocess.d.ts
CHANGED
package/dist/postprocess.js
CHANGED
|
@@ -1,17 +1,19 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { generateText, Output } from "ai";
|
|
2
2
|
import { z } from "zod";
|
|
3
3
|
import { withRetry } from "./utils/retry.js";
|
|
4
|
-
import {
|
|
5
|
-
const MODEL = "openai/gpt-oss-120b";
|
|
4
|
+
import { getProvider } from "./utils/providers.js";
|
|
6
5
|
const outputSchema = z.object({
|
|
7
6
|
fixed_transcription: z.string(),
|
|
8
7
|
});
|
|
9
8
|
export async function postprocess(rawTranscription, customPrompt, options) {
|
|
10
|
-
const { systemPrompt, customPromptPrefix, transcriptionPrefix } = options;
|
|
9
|
+
const { provider, modelName, systemPrompt, customPromptPrefix, transcriptionPrefix, } = options;
|
|
10
|
+
const providerInstance = getProvider(provider);
|
|
11
11
|
const result = await withRetry(async () => {
|
|
12
|
-
const response = await
|
|
13
|
-
model:
|
|
14
|
-
|
|
12
|
+
const response = await generateText({
|
|
13
|
+
model: providerInstance(modelName),
|
|
14
|
+
output: Output.object({
|
|
15
|
+
schema: outputSchema,
|
|
16
|
+
}),
|
|
15
17
|
messages: [
|
|
16
18
|
{
|
|
17
19
|
role: "system",
|
|
@@ -31,7 +33,7 @@ export async function postprocess(rawTranscription, customPrompt, options) {
|
|
|
31
33
|
},
|
|
32
34
|
],
|
|
33
35
|
});
|
|
34
|
-
return response.
|
|
36
|
+
return response.output;
|
|
35
37
|
}, 3, "postprocess");
|
|
36
38
|
return result.fixed_transcription;
|
|
37
39
|
}
|
package/dist/recorder.js
CHANGED
|
@@ -32,7 +32,7 @@ export async function record(verbose = false) {
|
|
|
32
32
|
return new Promise((resolve, reject) => {
|
|
33
33
|
// Initialize waveform buffer
|
|
34
34
|
let waveWidth = getWaveWidth();
|
|
35
|
-
const waveBuffer = new Array(waveWidth).fill(
|
|
35
|
+
const waveBuffer = new Array(waveWidth).fill(WAVE_CHARS[0]);
|
|
36
36
|
let currentDb = -60;
|
|
37
37
|
let cancelled = false;
|
|
38
38
|
// Spawn FFmpeg with ebur128 filter to get volume levels
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
export declare const groq: import("@ai-sdk/groq").GroqProvider;
|
|
2
|
+
export declare const anthropic: import("@ai-sdk/anthropic").AnthropicProvider;
|
|
3
|
+
export type ProviderType = "groq" | "anthropic";
|
|
4
|
+
export declare function getProvider(provider: ProviderType): import("@ai-sdk/groq").GroqProvider | import("@ai-sdk/anthropic").AnthropicProvider;
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { createGroq } from "@ai-sdk/groq";
|
|
2
|
+
import { createAnthropic } from "@ai-sdk/anthropic";
|
|
3
|
+
export const groq = createGroq();
|
|
4
|
+
export const anthropic = createAnthropic();
|
|
5
|
+
export function getProvider(provider) {
|
|
6
|
+
switch (provider) {
|
|
7
|
+
case "groq":
|
|
8
|
+
return groq;
|
|
9
|
+
case "anthropic":
|
|
10
|
+
return anthropic;
|
|
11
|
+
default:
|
|
12
|
+
throw new Error(`Unknown provider: ${provider}`);
|
|
13
|
+
}
|
|
14
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "whspr",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.10",
|
|
4
4
|
"description": "CLI tool for audio transcription with Groq Whisper API",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
@@ -29,8 +29,9 @@
|
|
|
29
29
|
"prepublishOnly": "npm run build"
|
|
30
30
|
},
|
|
31
31
|
"dependencies": {
|
|
32
|
-
"@ai-sdk/
|
|
33
|
-
"ai": "^
|
|
32
|
+
"@ai-sdk/anthropic": "^3.0.15",
|
|
33
|
+
"@ai-sdk/groq": "^3.0.11",
|
|
34
|
+
"ai": "^6.0.41",
|
|
34
35
|
"chalk": "^5.x",
|
|
35
36
|
"clipboardy": "^4.x",
|
|
36
37
|
"groq-sdk": "^0.x",
|