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 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
- systemPrompt: 'Your task is to clean up/fix transcribed text generated from mic input by the user according to the user\'s own prompt, this prompt may contain custom vocabulary, instructions, etc. Please return the user\'s transcription with the fixes made (e.g. the AI might hear "PostgreSQL" as "post crest QL" you need to use your own reasoning to fix these mistakes in the transcription)',
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
- if (fs.existsSync(SETTINGS_PATH)) {
23
- const content = fs.readFileSync(SETTINGS_PATH, "utf-8");
24
- return JSON.parse(content);
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
- // Check for API key before recording
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,
@@ -1,4 +1,7 @@
1
+ import { ProviderType } from "./utils/providers.js";
1
2
  export interface PostprocessOptions {
3
+ provider: ProviderType;
4
+ modelName: string;
2
5
  systemPrompt: string;
3
6
  customPromptPrefix: string;
4
7
  transcriptionPrefix: string;
@@ -1,17 +1,19 @@
1
- import { generateObject } from "ai";
1
+ import { generateText, Output } from "ai";
2
2
  import { z } from "zod";
3
3
  import { withRetry } from "./utils/retry.js";
4
- import { groq } from "./utils/groq.js";
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 generateObject({
13
- model: groq(MODEL),
14
- schema: outputSchema,
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.object;
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.6",
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/groq": "^1.x",
33
- "ai": "^4.x",
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",