@glossic/provider-anthropic 0.2.0 → 0.3.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/dist/index.js CHANGED
@@ -1,5 +1,5 @@
1
1
  import Anthropic from '@anthropic-ai/sdk';
2
- import { ProviderError, isProviderError } from '@glossic/schema';
2
+ import { ProviderError, isProviderError, looksLikeQuota } from '@glossic/schema';
3
3
 
4
4
  // src/index.ts
5
5
  var anthropicProviderName = "anthropic";
@@ -29,6 +29,15 @@ var toProviderError = (cause) => {
29
29
  cause
30
30
  });
31
31
  }
32
+ if (cause instanceof Anthropic.APIError && looksLikeQuota(cause.message)) {
33
+ return new ProviderError({
34
+ provider: anthropicProviderName,
35
+ code: "quota",
36
+ message: "the Anthropic account has no quota left",
37
+ detail: cause.message,
38
+ cause
39
+ });
40
+ }
32
41
  if (cause instanceof Anthropic.RateLimitError) {
33
42
  return new ProviderError({
34
43
  provider: anthropicProviderName,
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/index.ts"],"names":[],"mappings":"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","file":"index.js","sourcesContent":["import Anthropic from \"@anthropic-ai/sdk\";\nimport type { CompletionRequest, CompletionResult, Provider } from \"@glossic/schema\";\nimport { isProviderError, ProviderError } from \"@glossic/schema\";\n\nexport const anthropicProviderName = \"anthropic\";\nexport const ANTHROPIC_DEFAULT_MODEL = \"claude-opus-5\";\nconst DEFAULT_MAX_TOKENS = 16_000;\n\n/** Model prefixes that reject a sampling parameter outright, so temperature is dropped. */\nconst SAMPLING_REJECTED_BY = [\n \"claude-fable-\",\n \"claude-mythos-\",\n \"claude-opus-5\",\n \"claude-opus-4-7\",\n \"claude-opus-4-8\",\n \"claude-sonnet-5\",\n];\n\n/** Whether this model will take a temperature, rather than refusing the request. */\nexport const acceptsTemperature = (model: string): boolean =>\n !SAMPLING_REJECTED_BY.some((prefix) => model.startsWith(prefix));\n\nexport interface AnthropicProviderOptions {\n apiKey ?: string;\n baseUrl ?: string;\n model ?: string;\n maxTokens ?: number;\n createClient?: (apiKey: string) => Anthropic;\n}\n\n/** The API key from the options or the environment, empty treated as absent. */\nconst readApiKey = (options: AnthropicProviderOptions): string | undefined => {\n const key = options.apiKey ?? process.env.ANTHROPIC_API_KEY;\n\n return key === undefined || key.trim() === \"\" ? undefined : key;\n};\n\n/** Maps an SDK failure onto the shared error vocabulary, so retry knows what to do. */\nconst toProviderError = (cause: unknown): ProviderError => {\n if (isProviderError(cause)) return cause;\n\n if (cause instanceof Anthropic.AuthenticationError) {\n return new ProviderError({\n provider: anthropicProviderName,\n code : \"unauthenticated\",\n message : \"ANTHROPIC_API_KEY was rejected\",\n detail : cause.message,\n cause,\n });\n }\n\n if (cause instanceof Anthropic.RateLimitError) {\n return new ProviderError({\n provider: anthropicProviderName,\n code : \"rate-limit\",\n message : \"the Anthropic API rate limit was hit\",\n detail : cause.message,\n cause,\n });\n }\n\n if (cause instanceof Anthropic.APIConnectionError) {\n return new ProviderError({\n provider: anthropicProviderName,\n code : \"timeout\",\n message : \"could not reach the Anthropic API\",\n detail : cause.message,\n cause,\n });\n }\n\n if (cause instanceof Anthropic.APIError) {\n const status = cause.status ?? 0;\n return new ProviderError({\n provider: anthropicProviderName,\n code : status >= 500 ? \"server\" : \"api\",\n message : `the Anthropic API returned ${cause.status ?? \"an error\"}`,\n detail : cause.message,\n cause,\n });\n }\n\n return new ProviderError({\n provider: anthropicProviderName,\n code : \"api\",\n message : \"the Anthropic API call failed\",\n detail : cause instanceof Error ? cause.message: undefined,\n cause,\n });\n};\n\n/** The text blocks of a response, joined; other block types are dropped. */\nconst extractText = (message: Anthropic.Message): string => {\n return message.content\n .filter((block): block is Anthropic.TextBlock => block.type === \"text\")\n .map((block) => block.text)\n .join(\"\");\n}\n\n\n/**\n * Provider backed by the Anthropic API. Reports itself unavailable when no key\n * is set, which is how auto-detection falls through to the next one.\n */\nexport const createAnthropicProvider = (options: AnthropicProviderOptions = {}): Provider => {\n const model = options.model ?? ANTHROPIC_DEFAULT_MODEL;\n const maxTokens = options.maxTokens ?? DEFAULT_MAX_TOKENS;\n \n const createClient = options.createClient ?? ((apiKey: string) => new Anthropic({\n apiKey,\n ...(options.baseUrl === undefined ? {} : { baseURL: options.baseUrl }),\n }));\n\n let client: Anthropic | undefined;\n\n const requireClient = (): Anthropic => {\n const apiKey = readApiKey(options);\n\n if (apiKey === undefined) {\n throw new ProviderError({\n provider: anthropicProviderName,\n code : \"unauthenticated\",\n message : \"ANTHROPIC_API_KEY is not set\",\n detail : \"Create a key at https://console.anthropic.com/settings/keys and export it.\",\n });\n }\n\n client ??= createClient(apiKey);\n return client;\n };\n\n return {\n name: anthropicProviderName,\n\n available: async (): Promise<boolean> => readApiKey(options) !== undefined,\n\n complete: async (request: CompletionRequest): Promise<CompletionResult> => {\n const requestModel = request.model ?? model;\n const temperature = request.temperature ?? 0;\n\n let message: Anthropic.Message;\n try {\n message = await requireClient().messages.create({\n model : requestModel,\n max_tokens: request.maxTokens ?? maxTokens,\n ...(request.system === undefined ? {} : { system: request.system }),\n ...(acceptsTemperature(requestModel) ? { temperature } : {}),\n messages: [{ role: \"user\", content: request.prompt }],\n });\n } catch (cause) {\n throw toProviderError(cause);\n }\n\n if (message.stop_reason === \"refusal\") {\n throw new ProviderError({\n provider: anthropicProviderName,\n code : \"refused\",\n message : \"the model declined to answer\",\n detail : `stop_reason=refusal on ${message.model}`,\n });\n }\n\n return {\n text : extractText(message),\n model: message.model,\n usage: {\n inputTokens : message.usage.input_tokens,\n outputTokens: message.usage.output_tokens,\n ...(message.usage.cache_read_input_tokens === null ||\n message.usage.cache_read_input_tokens === undefined\n ? {}\n : { cacheReadTokens: message.usage.cache_read_input_tokens }),\n },\n raw: message,\n };\n },\n };\n};\n\n/** The default-configured instance, for callers with nothing to override. */\nexport const anthropicProvider: Provider = createAnthropicProvider();\n\nexport default anthropicProvider;\n"]}
1
+ {"version":3,"sources":["../src/index.ts"],"names":[],"mappings":"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","file":"index.js","sourcesContent":["import Anthropic from \"@anthropic-ai/sdk\";\nimport type { CompletionRequest, CompletionResult, Provider } from \"@glossic/schema\";\nimport { isProviderError, looksLikeQuota, ProviderError } from \"@glossic/schema\";\n\nexport const anthropicProviderName = \"anthropic\";\nexport const ANTHROPIC_DEFAULT_MODEL = \"claude-opus-5\";\nconst DEFAULT_MAX_TOKENS = 16_000;\n\n/** Model prefixes that reject a sampling parameter outright, so temperature is dropped. */\nconst SAMPLING_REJECTED_BY = [\n \"claude-fable-\",\n \"claude-mythos-\",\n \"claude-opus-5\",\n \"claude-opus-4-7\",\n \"claude-opus-4-8\",\n \"claude-sonnet-5\",\n];\n\n/** Whether this model will take a temperature, rather than refusing the request. */\nexport const acceptsTemperature = (model: string): boolean =>\n !SAMPLING_REJECTED_BY.some((prefix) => model.startsWith(prefix));\n\nexport interface AnthropicProviderOptions {\n apiKey ?: string;\n baseUrl ?: string;\n model ?: string;\n maxTokens ?: number;\n createClient?: (apiKey: string) => Anthropic;\n}\n\n/** The API key from the options or the environment, empty treated as absent. */\nconst readApiKey = (options: AnthropicProviderOptions): string | undefined => {\n const key = options.apiKey ?? process.env.ANTHROPIC_API_KEY;\n\n return key === undefined || key.trim() === \"\" ? undefined : key;\n};\n\n/** Maps an SDK failure onto the shared error vocabulary, so retry knows what to do. */\nconst toProviderError = (cause: unknown): ProviderError => {\n if (isProviderError(cause)) return cause;\n\n if (cause instanceof Anthropic.AuthenticationError) {\n return new ProviderError({\n provider: anthropicProviderName,\n code : \"unauthenticated\",\n message : \"ANTHROPIC_API_KEY was rejected\",\n detail : cause.message,\n cause,\n });\n }\n\n if (cause instanceof Anthropic.APIError && looksLikeQuota(cause.message)) {\n return new ProviderError({\n provider: anthropicProviderName,\n code : \"quota\",\n message : \"the Anthropic account has no quota left\",\n detail : cause.message,\n cause,\n });\n }\n\n if (cause instanceof Anthropic.RateLimitError) {\n return new ProviderError({\n provider: anthropicProviderName,\n code : \"rate-limit\",\n message : \"the Anthropic API rate limit was hit\",\n detail : cause.message,\n cause,\n });\n }\n\n if (cause instanceof Anthropic.APIConnectionError) {\n return new ProviderError({\n provider: anthropicProviderName,\n code : \"timeout\",\n message : \"could not reach the Anthropic API\",\n detail : cause.message,\n cause,\n });\n }\n\n if (cause instanceof Anthropic.APIError) {\n const status = cause.status ?? 0;\n return new ProviderError({\n provider: anthropicProviderName,\n code : status >= 500 ? \"server\" : \"api\",\n message : `the Anthropic API returned ${cause.status ?? \"an error\"}`,\n detail : cause.message,\n cause,\n });\n }\n\n return new ProviderError({\n provider: anthropicProviderName,\n code : \"api\",\n message : \"the Anthropic API call failed\",\n detail : cause instanceof Error ? cause.message: undefined,\n cause,\n });\n};\n\n/** The text blocks of a response, joined; other block types are dropped. */\nconst extractText = (message: Anthropic.Message): string => {\n return message.content\n .filter((block): block is Anthropic.TextBlock => block.type === \"text\")\n .map((block) => block.text)\n .join(\"\");\n}\n\n\n/**\n * Provider backed by the Anthropic API. Reports itself unavailable when no key\n * is set, which is how auto-detection falls through to the next one.\n */\nexport const createAnthropicProvider = (options: AnthropicProviderOptions = {}): Provider => {\n const model = options.model ?? ANTHROPIC_DEFAULT_MODEL;\n const maxTokens = options.maxTokens ?? DEFAULT_MAX_TOKENS;\n \n const createClient = options.createClient ?? ((apiKey: string) => new Anthropic({\n apiKey,\n ...(options.baseUrl === undefined ? {} : { baseURL: options.baseUrl }),\n }));\n\n let client: Anthropic | undefined;\n\n const requireClient = (): Anthropic => {\n const apiKey = readApiKey(options);\n\n if (apiKey === undefined) {\n throw new ProviderError({\n provider: anthropicProviderName,\n code : \"unauthenticated\",\n message : \"ANTHROPIC_API_KEY is not set\",\n detail : \"Create a key at https://console.anthropic.com/settings/keys and export it.\",\n });\n }\n\n client ??= createClient(apiKey);\n return client;\n };\n\n return {\n name: anthropicProviderName,\n\n available: async (): Promise<boolean> => readApiKey(options) !== undefined,\n\n complete: async (request: CompletionRequest): Promise<CompletionResult> => {\n const requestModel = request.model ?? model;\n const temperature = request.temperature ?? 0;\n\n let message: Anthropic.Message;\n try {\n message = await requireClient().messages.create({\n model : requestModel,\n max_tokens: request.maxTokens ?? maxTokens,\n ...(request.system === undefined ? {} : { system: request.system }),\n ...(acceptsTemperature(requestModel) ? { temperature } : {}),\n messages: [{ role: \"user\", content: request.prompt }],\n });\n } catch (cause) {\n throw toProviderError(cause);\n }\n\n if (message.stop_reason === \"refusal\") {\n throw new ProviderError({\n provider: anthropicProviderName,\n code : \"refused\",\n message : \"the model declined to answer\",\n detail : `stop_reason=refusal on ${message.model}`,\n });\n }\n\n return {\n text : extractText(message),\n model: message.model,\n usage: {\n inputTokens : message.usage.input_tokens,\n outputTokens: message.usage.output_tokens,\n ...(message.usage.cache_read_input_tokens === null ||\n message.usage.cache_read_input_tokens === undefined\n ? {}\n : { cacheReadTokens: message.usage.cache_read_input_tokens }),\n },\n raw: message,\n };\n },\n };\n};\n\n/** The default-configured instance, for callers with nothing to override. */\nexport const anthropicProvider: Provider = createAnthropicProvider();\n\nexport default anthropicProvider;\n"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@glossic/provider-anthropic",
3
- "version": "0.2.0",
3
+ "version": "0.3.0",
4
4
  "description": "Completion provider backed by the Anthropic API",
5
5
  "keywords": [
6
6
  "documentation",
@@ -46,7 +46,7 @@
46
46
  },
47
47
  "dependencies": {
48
48
  "@anthropic-ai/sdk": "^0.71.0",
49
- "@glossic/schema": "0.2.0"
49
+ "@glossic/schema": "0.3.0"
50
50
  },
51
51
  "scripts": {
52
52
  "build": "tsup",