defiant-jazz 0.6.2 → 0.7.0

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/README.md CHANGED
@@ -101,6 +101,42 @@ Per-call overrides work on both forms:
101
101
  await refine.dylan("Hello world", { model: 'claude-opus-5', maxTokens: 1024 });
102
102
  ```
103
103
 
104
+ ### Effort (Anthropic only)
105
+
106
+ `effort` controls how much the model reasons before answering: `'low'`,
107
+ `'medium'`, `'high'`, `'xhigh'` or `'max'`. Lower is cheaper and faster. Leave
108
+ it unset to use the model's own default, which varies by model.
109
+
110
+ ```typescript
111
+ const fast = createDefiantJazz({ effort: 'low' });
112
+ await refine.dylan("Hello world", { effort: 'low' }); // per-call works too
113
+ ```
114
+
115
+ - Setting `effort` with the OpenAI provider throws.
116
+ - Not every model accepts every level (older models may reject `effort`
117
+ entirely); the API's error is passed through.
118
+ - Reasoning tokens count toward `maxTokens`. At `xhigh` or `max`, raise
119
+ `maxTokens` if you see "returned no text content (stop_reason: max_tokens)".
120
+
121
+ ## Usage and cost
122
+
123
+ Every result includes the model that answered and the request's token
124
+ counts, normalized across providers:
125
+
126
+ ```typescript
127
+ const result = await refine.dylan("Hello world");
128
+ // {
129
+ // text: "...",
130
+ // character: "Dylan G.",
131
+ // model: "claude-sonnet-5",
132
+ // usage: { inputTokens: 2770, outputTokens: 77 },
133
+ // }
134
+ ```
135
+
136
+ `outputTokens` includes any reasoning tokens the model spent before answering
137
+ (see [Effort](#effort-anthropic-only)). `usage` is omitted if the provider
138
+ doesn't report it.
139
+
104
140
  ## Characters
105
141
 
106
142
  ```typescript
package/dist/client.js CHANGED
@@ -51,13 +51,17 @@ export function createDefiantJazz(initialOptions = {}) {
51
51
  }
52
52
  const characterName = CHARACTERS[characterKey];
53
53
  const { provider, client } = getClient();
54
- const defaults = { model: config.model, maxTokens: config.maxTokens };
54
+ const effort = options?.effort ?? config.effort;
55
+ if (effort !== undefined && provider === "openai") {
56
+ throw new Error("effort is only supported with the Anthropic provider.");
57
+ }
58
+ const defaults = { model: config.model, maxTokens: config.maxTokens, effort: config.effort };
55
59
  const lore = options?.lore ?? config.lore ?? true;
56
60
  const systemPrompt = buildSystemPrompt(lore);
57
- const resultText = provider === "anthropic"
61
+ const result = provider === "anthropic"
58
62
  ? await runAnthropic(client, characterName, text, systemPrompt, defaults, options)
59
63
  : await runOpenAi(client, characterName, text, systemPrompt, defaults, options);
60
- return { text: resultText, character: characterName };
64
+ return { ...result, character: characterName };
61
65
  }
62
66
  const fn = ((text, character, options) => transform(text, character, options));
63
67
  fn.mark = (text, options) => transform(text, "mark", options);
package/dist/index.d.ts CHANGED
@@ -1,7 +1,7 @@
1
1
  import { createDefiantJazz, type DefiantJazz } from "./client.js";
2
2
  import { CHARACTERS } from "./prompt.js";
3
- import type { CharacterKey, DefiantJazzOptions, RefineOptions, RefineResult } from "./types.js";
4
- export type { CharacterKey, DefiantJazzOptions, RefineOptions, RefineResult };
3
+ import type { CharacterKey, DefiantJazzOptions, Effort, RefineOptions, RefineResult, TokenUsage } from "./types.js";
4
+ export type { CharacterKey, DefiantJazzOptions, Effort, RefineOptions, RefineResult, TokenUsage, };
5
5
  export { CHARACTERS, createDefiantJazz };
6
6
  /**
7
7
  * Configure the default `refine` instance with an API key and optional
@@ -1,7 +1,8 @@
1
1
  import Anthropic from "@anthropic-ai/sdk";
2
- import type { RefineOptions } from "../types.js";
2
+ import type { Effort, RefineOptions, RefineResult } from "../types.js";
3
3
  export declare function createAnthropicClient(apiKey: string): Anthropic;
4
4
  export declare function runAnthropic(client: Anthropic, characterName: string, text: string, systemPrompt: string, defaults: {
5
5
  model?: string;
6
6
  maxTokens?: number;
7
- }, options?: RefineOptions): Promise<string>;
7
+ effort?: Effort;
8
+ }, options?: RefineOptions): Promise<Omit<RefineResult, "character">>;
@@ -5,10 +5,12 @@ export function createAnthropicClient(apiKey) {
5
5
  return new Anthropic({ apiKey });
6
6
  }
7
7
  export async function runAnthropic(client, characterName, text, systemPrompt, defaults, options) {
8
+ const effort = options?.effort ?? defaults.effort;
8
9
  try {
9
10
  const response = await client.messages.create({
10
11
  model: options?.model ?? defaults.model ?? DEFAULT_MODEL,
11
12
  max_tokens: options?.maxTokens ?? defaults.maxTokens ?? DEFAULT_MAX_TOKENS,
13
+ ...(effort !== undefined && { output_config: { effort } }),
12
14
  system: systemPrompt,
13
15
  messages: [
14
16
  {
@@ -21,7 +23,14 @@ export async function runAnthropic(client, characterName, text, systemPrompt, de
21
23
  if (!textBlock) {
22
24
  throw new Error(`Anthropic returned no text content (stop_reason: ${response.stop_reason})`);
23
25
  }
24
- return textBlock.text;
26
+ return {
27
+ text: textBlock.text,
28
+ model: response.model,
29
+ usage: response.usage && {
30
+ inputTokens: response.usage.input_tokens,
31
+ outputTokens: response.usage.output_tokens,
32
+ },
33
+ };
25
34
  }
26
35
  catch (error) {
27
36
  if (error instanceof Anthropic.AuthenticationError) {
@@ -1,7 +1,7 @@
1
1
  import OpenAI from "openai";
2
- import type { RefineOptions } from "../types.js";
2
+ import type { RefineOptions, RefineResult } from "../types.js";
3
3
  export declare function createOpenAiClient(apiKey: string): OpenAI;
4
4
  export declare function runOpenAi(client: OpenAI, characterName: string, text: string, systemPrompt: string, defaults: {
5
5
  model?: string;
6
6
  maxTokens?: number;
7
- }, options?: RefineOptions): Promise<string>;
7
+ }, options?: RefineOptions): Promise<Omit<RefineResult, "character">>;
@@ -20,5 +20,12 @@ export async function runOpenAi(client, characterName, text, systemPrompt, defau
20
20
  if (!content) {
21
21
  throw new Error("OpenAI returned no content.");
22
22
  }
23
- return content;
23
+ return {
24
+ text: content,
25
+ model: completion.model,
26
+ usage: completion.usage && {
27
+ inputTokens: completion.usage.prompt_tokens,
28
+ outputTokens: completion.usage.completion_tokens,
29
+ },
30
+ };
24
31
  }
package/dist/types.d.ts CHANGED
@@ -1,8 +1,20 @@
1
+ import type Anthropic from "@anthropic-ai/sdk";
1
2
  export type CharacterKey = "mark" | "irving" | "dylan" | "milchick";
2
3
  export type Provider = "anthropic" | "openai";
4
+ /** Anthropic effort level, taken from the SDK so new levels arrive with SDK updates. */
5
+ export type Effort = NonNullable<Anthropic.OutputConfig["effort"]>;
6
+ export interface TokenUsage {
7
+ inputTokens: number;
8
+ /** Includes any reasoning tokens the model spent before answering. */
9
+ outputTokens: number;
10
+ }
3
11
  export interface RefineResult {
4
12
  text: string;
5
13
  character: string;
14
+ /** The model that answered, as reported by the provider. */
15
+ model: string;
16
+ /** Token counts for this request, when the provider reports them. */
17
+ usage?: TokenUsage;
6
18
  }
7
19
  export interface DefiantJazzOptions {
8
20
  /** Explicit provider. Defaults to whichever API key is set (Anthropic first). */
@@ -10,6 +22,12 @@ export interface DefiantJazzOptions {
10
22
  apiKey?: string;
11
23
  model?: string;
12
24
  maxTokens?: number;
25
+ /**
26
+ * How much the model reasons before answering (Anthropic only). Lower is
27
+ * cheaper and faster. Leave unset to use the model's own default, which
28
+ * varies by model. Setting it with the OpenAI provider throws.
29
+ */
30
+ effort?: Effort;
13
31
  /**
14
32
  * Whether characters reach for Lumon/Severance vocabulary (the break room,
15
33
  * waffle parties, Kier, innie/outie, etc.) when a quote's situation calls
@@ -21,6 +39,11 @@ export interface DefiantJazzOptions {
21
39
  export interface RefineOptions {
22
40
  model?: string;
23
41
  maxTokens?: number;
42
+ /**
43
+ * How much the model reasons before answering (Anthropic only). Overrides
44
+ * the instance-level setting. Leave unset to use the model's own default.
45
+ */
46
+ effort?: Effort;
24
47
  signal?: AbortSignal;
25
48
  /**
26
49
  * Whether this call reaches for Lumon/Severance vocabulary. Overrides the
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "defiant-jazz",
3
- "version": "0.6.2",
3
+ "version": "0.7.0",
4
4
  "description": "Transform quotes into Severance character voices using Claude or OpenAI",
5
5
  "license": "MIT",
6
6
  "author": "pelikenjones",