npm-ai-hooks 1.0.0 → 1.0.1

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.
Files changed (71) hide show
  1. package/.env.example +14 -0
  2. package/CODE_OF_CONDUCT.md +30 -0
  3. package/CONTRIBUTING.md +88 -0
  4. package/EXAMPLES.md +53 -0
  5. package/ROADMAP.md +22 -0
  6. package/SECURITY.md +30 -0
  7. package/examples/basic/explain.ts +18 -0
  8. package/examples/basic/rewrite.ts +10 -0
  9. package/examples/basic/sentiment.ts +10 -0
  10. package/examples/basic/summarize.ts +10 -0
  11. package/examples/basic/translate.ts +10 -0
  12. package/examples/demo.ts +16 -0
  13. package/examples/model-switch/groq-default.ts +10 -0
  14. package/examples/model-switch/groq-text2.ts +10 -0
  15. package/examples/model-switch/openrouter-default.ts +10 -0
  16. package/examples/model-switch/openrouter-gpt5.ts +10 -0
  17. package/examples/model-switch/wrong-models.ts +21 -0
  18. package/examples/openrouter/summarize-switch-model-demo.ts +25 -0
  19. package/examples/openrouter/translate-switch-model-demo.ts +24 -0
  20. package/examples/openrouter/translate-to-urdu-demo.ts +19 -0
  21. package/examples/openrouter-openai/gpt5-demo.ts +25 -0
  22. package/jest.config.js +11 -0
  23. package/package.json +5 -16
  24. package/src/errors.ts +23 -0
  25. package/{dist/index.d.ts → src/index.ts} +2 -1
  26. package/src/providers/claude.ts +101 -0
  27. package/src/providers/deepkseek.ts +98 -0
  28. package/src/providers/gemini.ts +99 -0
  29. package/src/providers/groq.ts +98 -0
  30. package/src/providers/index.ts +84 -0
  31. package/src/providers/mistral.ts +98 -0
  32. package/src/providers/openai.ts +98 -0
  33. package/src/providers/openrouter.ts +100 -0
  34. package/src/providers/perplexity.ts +98 -0
  35. package/src/providers/xai.ts +98 -0
  36. package/src/types/claude.ts +25 -0
  37. package/src/types/core/providers.ts +18 -0
  38. package/src/types/deepseek.ts +11 -0
  39. package/src/types/gemini.ts +30 -0
  40. package/src/types/groq.ts +20 -0
  41. package/src/types/index.ts +55 -0
  42. package/src/types/mistral.ts +48 -0
  43. package/src/types/openai.ts +46 -0
  44. package/src/types/openrouter.ts +52 -0
  45. package/src/types/perplexity.ts +16 -0
  46. package/src/types/xai.ts +19 -0
  47. package/src/wrap.ts +93 -0
  48. package/tsconfig.json +11 -0
  49. package/dist/errors.d.ts +0 -8
  50. package/dist/errors.js +0 -21
  51. package/dist/index.js +0 -6
  52. package/dist/providers/groq.d.ts +0 -2
  53. package/dist/providers/groq.js +0 -57
  54. package/dist/providers/index.d.ts +0 -7
  55. package/dist/providers/index.js +0 -45
  56. package/dist/providers/openai.d.ts +0 -1
  57. package/dist/providers/openai.js +0 -19
  58. package/dist/providers/openrouter.d.ts +0 -1
  59. package/dist/providers/openrouter.js +0 -55
  60. package/dist/types/core/providers.d.ts +0 -10
  61. package/dist/types/core/providers.js +0 -2
  62. package/dist/types/groq.d.ts +0 -2
  63. package/dist/types/groq.js +0 -4
  64. package/dist/types/index.d.ts +0 -24
  65. package/dist/types/index.js +0 -11
  66. package/dist/types/openai.d.ts +0 -2
  67. package/dist/types/openai.js +0 -4
  68. package/dist/types/openrouter.d.ts +0 -2
  69. package/dist/types/openrouter.js +0 -4
  70. package/dist/wrap.d.ts +0 -5
  71. package/dist/wrap.js +0 -85
package/.env.example ADDED
@@ -0,0 +1,14 @@
1
+ # OpenAI API key (for OpenRouter OpenAI models or standalone OpenAI)
2
+ AI_HOOK_OPENAI_KEY=your_openai_api_key_here
3
+
4
+ # OpenRouter API key
5
+ AI_HOOK_OPENROUTER_KEY=your_openrouter_api_key_here
6
+
7
+ # Groq API key
8
+ AI_HOOK_GROQ_KEY=your_groq_api_key_here
9
+
10
+ # Default provider (optional)
11
+ # AI_HOOK_DEFAULT_PROVIDER=openrouter
12
+
13
+ # Default model is handled by code, so no need to set it here
14
+ # AI_HOOK_DEFAULT_MODEL=
@@ -0,0 +1,30 @@
1
+ ```md
2
+ # Contributor Covenant Code of Conduct
3
+
4
+ ## Our Pledge
5
+
6
+ We as members, contributors, and maintainers pledge to make participation in this project a harassment-free experience for everyone, regardless of age, body size, visible or invisible disability, ethnicity, sex characteristics, gender identity and expression, level of experience, education, socio-economic status, nationality, personal appearance, race, religion, or sexual identity and orientation.
7
+
8
+ ## Our Standards
9
+
10
+ Examples of behavior that contributes to a positive environment include:
11
+
12
+ - Using welcoming and inclusive language
13
+ - Being respectful of differing viewpoints and experiences
14
+ - Gracefully accepting constructive criticism
15
+ - Focusing on what is best for the community
16
+ - Showing empathy towards other community members
17
+
18
+ Examples of unacceptable behavior include:
19
+
20
+ - Harassment of public or private forms
21
+ - Trolling, insulting/derogatory comments
22
+ - Publishing others’ private information without permission
23
+ - Other conduct which could reasonably be considered inappropriate
24
+
25
+ ## Enforcement
26
+
27
+ Instances of abusive behavior may be reported to the maintainers via [issues](https://github.com/RealTeebot/npm-ai-hooks/issues).
28
+ All complaints will be reviewed and addressed appropriately.
29
+
30
+ This Code of Conduct is adapted from the [Contributor Covenant](https://www.contributor-covenant.org/).
@@ -0,0 +1,88 @@
1
+ # Contributing to npm-ai-hooks
2
+
3
+ 👋 Thanks for your interest in contributing to **npm-ai-hooks**!
4
+ This project is maintained by the [RealTeebot](https://github.com/RealTeebot) organization and welcomes community involvement at all levels — from bug fixes and documentation to major feature development.
5
+
6
+ ---
7
+
8
+ ## 🛠️ Development Setup
9
+
10
+ 1. **Fork** the repository and clone your fork:
11
+
12
+ ```bash
13
+ git clone https://github.com/your-username/npm-ai-hooks.git
14
+ cd npm-ai-hooks
15
+
16
+ npm install
17
+
18
+ npm run dev
19
+
20
+ npm run test
21
+
22
+ npm-ai-hooks/
23
+ ├─ src/
24
+ │ ├─ index.ts # Main entry
25
+ │ ├─ wrap.ts # Core hook wrapper logic
26
+ │ ├─ cache.ts # Caching engine
27
+ │ ├─ cost.ts # Cost estimation utilities
28
+ │ ├─ errors.ts # Unified error handling
29
+ │ ├─ providers/ # Provider adapters
30
+ │ │ ├─ openai.ts
31
+ │ │ ├─ claude.ts
32
+ │ │ └─ ...
33
+ ├─ examples/
34
+ │ ├─ node-basic/
35
+ │ └─ react-basic/
36
+ ├─ tests/
37
+ └─ ...
38
+
39
+ ✨ Contributing Guidelines
40
+
41
+ TypeScript Only: All code must be written in TypeScript with strict typing enabled.
42
+
43
+ Linting: Run npm run lint before committing.
44
+
45
+ Formatting: Code must pass Prettier formatting checks.
46
+
47
+ Commit Style: Use Conventional Commits
48
+ (e.g., feat: add caching to DeepSeek provider).
49
+
50
+ Tests: Add unit/integration tests for any new features or bug fixes.
51
+
52
+ 📦 Adding a New Provider
53
+
54
+ Create a new file in src/providers/ (e.g., mistral.ts)
55
+
56
+ Implement the required interface:
57
+
58
+ export const mistralProvider: Provider = {
59
+ name: "mistral",
60
+ isAvailable: () => !!process.env.AI_HOOK_MISTRAL_KEY,
61
+ generate: async (prompt, options) => { /* API call logic */ },
62
+ models: ["mistral-medium", "mistral-large"]
63
+ };
64
+
65
+
66
+ Register the provider in providers/index.ts
67
+
68
+ Add documentation and tests.
69
+
70
+ 🧪 Testing
71
+
72
+ We use Jest for unit testing and Playwright for integration tests where relevant.
73
+
74
+ npm run test
75
+ npm run test:watch
76
+
77
+ 📣 Communication
78
+
79
+ Issues: GitHub Issues
80
+
81
+ Discussions: GitHub Discussions
82
+
83
+ Pull Requests: Always create a PR to the main branch
84
+
85
+ 🙏 Credits
86
+
87
+ Maintained with ❤️ by RealTeebot
88
+ and contributors.
package/EXAMPLES.md ADDED
@@ -0,0 +1,53 @@
1
+ 7. EXAMPLES.md
2
+ # Usage Examples – npm-ai-hooks
3
+
4
+ ## 🧠 In a React Frontend
5
+
6
+ Install:
7
+
8
+ ```bash
9
+ npm install npm-ai-hooks
10
+
11
+
12
+ Example:
13
+
14
+ import { wrap } from "npm-ai-hooks";
15
+
16
+ const summarize = wrap((text: string) => text, { task: "summarize" });
17
+
18
+ export function App() {
19
+ const [result, setResult] = useState("");
20
+
21
+ const handleClick = async () => {
22
+ const res = await summarize("Explain server components in React.");
23
+ setResult(res.output);
24
+ };
25
+
26
+ return (
27
+ <div>
28
+ <button onClick={handleClick}>Summarize</button>
29
+ <p>{result}</p>
30
+ </div>
31
+ );
32
+ }
33
+
34
+ ⚙️ In a Node.js Backend
35
+ import { wrap } from "npm-ai-hooks";
36
+
37
+ const explain = wrap((input: string) => input, { task: "explain" });
38
+
39
+ app.post("/explain", async (req, res) => {
40
+ const result = await explain(req.body.text);
41
+ res.json(result);
42
+ });
43
+
44
+
45
+ This works with Express, NestJS, Fastify, Next.js API routes — anything Node-compatible.
46
+
47
+
48
+ ---
49
+
50
+
51
+ ---
52
+
53
+ ✅ **With these files in place**, your repository will be structured like a professional, enterprise-ready open-source project:
package/ROADMAP.md ADDED
@@ -0,0 +1,22 @@
1
+ # Roadmap – npm-ai-hooks
2
+
3
+ ✅ v0.1.0 (MVP)
4
+ - [x] Basic `wrap()` functionality
5
+ - [x] OpenAI + Claude support
6
+ - [x] Error handling and cost estimation
7
+ - [x] Caching layer
8
+
9
+ 🚀 v0.2.0
10
+ - [ ] Provider auto-discovery CLI
11
+ - [ ] More built-in tasks (e.g., classify, code explain)
12
+ - [ ] React hook wrapper (`useAI`)
13
+
14
+ 💡 v0.3.0
15
+ - [ ] Streaming support
16
+ - [ ] Local model support (Ollama / Llama.cpp)
17
+ - [ ] SDK for Python (bridge)
18
+
19
+ 🧠 Long-Term Vision
20
+ - Unified dashboard for usage + cost
21
+ - Plugin ecosystem (community providers, custom tasks)
22
+ - Serverless deployment starter template
package/SECURITY.md ADDED
@@ -0,0 +1,30 @@
1
+ # Security Policy
2
+
3
+ ## Supported Versions
4
+
5
+ We actively support the latest major release and security patches for the previous one.
6
+
7
+ | Version | Supported |
8
+ |--------|-----------|
9
+ | 1.x.x | ✅ |
10
+ | 0.x.x | ❌ |
11
+
12
+ ---
13
+
14
+ ## Reporting a Vulnerability
15
+
16
+ If you discover a security vulnerability, **DO NOT** open a public GitHub issue.
17
+
18
+ Instead, please report it privately by emailing:
19
+ **security@realteebot.dev**
20
+
21
+ We take all security issues seriously and will respond within 48 hours.
22
+
23
+ ---
24
+
25
+ ## Disclosure Process
26
+
27
+ 1. We confirm and investigate the vulnerability.
28
+ 2. We create a patch and test the fix.
29
+ 3. We coordinate a release and notify users.
30
+ 4. We credit the reporter (if they wish).
@@ -0,0 +1,18 @@
1
+ import { wrap } from "../../src/wrap";
2
+
3
+ const explain = wrap((text: string) => text, { task: "explain" });
4
+
5
+ async function run() {
6
+ try {
7
+ const result = await explain("Quantum computing is complex.");
8
+ console.log(result.output);
9
+ } catch (err: any) {
10
+ if (err && typeof err.pretty === "function") {
11
+ err.pretty();
12
+ } else {
13
+ console.error(err.message || err);
14
+ }
15
+ }
16
+ }
17
+
18
+ run();
@@ -0,0 +1,10 @@
1
+ import { wrap } from "../../src/wrap";
2
+
3
+ const rewrite = wrap((text: string) => text, { task: "rewrite" });
4
+
5
+ async function run() {
6
+ const result = await rewrite("AI code can sometimes be confusing.");
7
+ console.log(result.output);
8
+ }
9
+
10
+ run();
@@ -0,0 +1,10 @@
1
+ import { wrap } from "../../src/wrap";
2
+
3
+ const sentiment = wrap((text: string) => text, { task: "sentiment" });
4
+
5
+ async function run() {
6
+ const result = await sentiment("I love using AI-Hooks for my projects!");
7
+ console.log(result.output);
8
+ }
9
+
10
+ run();
@@ -0,0 +1,10 @@
1
+ import { wrap } from "../../src/wrap";
2
+
3
+ const summarize = wrap((text: string) => text, { task: "summarize" });
4
+
5
+ async function run() {
6
+ const result = await summarize("OpenRouter is a powerful AI integration tool.");
7
+ console.log(result.output);
8
+ }
9
+
10
+ run();
@@ -0,0 +1,10 @@
1
+ import { wrap } from "../../src/wrap";
2
+
3
+ const translate = wrap((text: string) => text, { task: "translate", targetLanguage: "Urdu" });
4
+
5
+ async function run() {
6
+ const result = await translate("Hello everyone! Welcome to AI demos.");
7
+ console.log(result.output);
8
+ }
9
+
10
+ run();
@@ -0,0 +1,16 @@
1
+ import { wrap } from "../src";
2
+
3
+ const summarize = wrap((text: string) => text, { task: "summarize" });
4
+
5
+ (async () => {
6
+ try {
7
+ const result = await summarize("Gemini is made by..");
8
+ console.log(result.output);
9
+ } catch (err: any) {
10
+ if (err.pretty) {
11
+ err.pretty(); // prints nice error with suggestions
12
+ } else {
13
+ console.error(err);
14
+ }
15
+ }
16
+ })();
@@ -0,0 +1,10 @@
1
+ import { wrap } from "../../src/wrap";
2
+
3
+ const explain = wrap((text: string) => text, { task: "explain", provider: "groq" });
4
+
5
+ async function run() {
6
+ const result = await explain("Groq provides fast text AI models.");
7
+ console.log(result.output);
8
+ }
9
+
10
+ run();
@@ -0,0 +1,10 @@
1
+ import { wrap } from "../../src/wrap";
2
+
3
+ const explain = wrap((text: string) => text, { task: "explain", provider: "groq", model: "allam-2-7b" });
4
+
5
+ async function run() {
6
+ const result = await explain("Groq's text-2 model is versatile for many tasks.");
7
+ console.log(result.output);
8
+ }
9
+
10
+ run();
@@ -0,0 +1,10 @@
1
+ import { wrap } from "../../src/wrap";
2
+
3
+ const summarize = wrap((text: string) => text, { task: "summarize", provider: "openrouter" });
4
+
5
+ async function run() {
6
+ const result = await summarize("OpenRouter makes AI integration seamless.");
7
+ console.log(result.output);
8
+ }
9
+
10
+ run();
@@ -0,0 +1,10 @@
1
+ import { wrap } from "../../src/wrap";
2
+
3
+ const summarize = wrap((text: string) => text, { task: "summarize", provider: "openrouter", model: "openai/gpt-5-pro" });
4
+
5
+ async function run() {
6
+ const result = await summarize("OpenRouter supports multiple models including GPT-5.");
7
+ console.log(result.output);
8
+ }
9
+
10
+ run();
@@ -0,0 +1,21 @@
1
+ import { wrap } from "../../src/wrap";
2
+
3
+ async function run() {
4
+ // Wrong OpenRouter model
5
+ try {
6
+ const wrongOpenRouter = wrap((text: string) => text, { task: "summarize", provider: "openrouter", model: "invalid-model" as any });
7
+ await wrongOpenRouter("Test text");
8
+ } catch (err: unknown) {
9
+ console.error("❌ OpenRouter error:", err);
10
+ }
11
+
12
+ // Wrong Groq model
13
+ try {
14
+ const wrongGroq = wrap((text: string) => text, { task: "explain", provider: "groq", model: "invalid-groq-model" as any });
15
+ await wrongGroq("Test text");
16
+ } catch (err: unknown) {
17
+ console.error("❌ Groq error:", err);
18
+ }
19
+ }
20
+
21
+ run();
@@ -0,0 +1,25 @@
1
+ import dotenv from "dotenv";
2
+ dotenv.config();
3
+ import { wrap } from "../../src/wrap";
4
+ import { OpenRouterModel } from "../../src/types/openrouter";
5
+
6
+ const summarize = (model: OpenRouterModel) =>
7
+ wrap((text: string) => text, { task: "summarize", provider: "openrouter", model });
8
+
9
+ async function run() {
10
+ const text = "OpenRouter allows developers to switch AI models easily.";
11
+
12
+ const models: OpenRouterModel[] = [
13
+ "openai/gpt-oss-20b:free",
14
+ "openai/gpt-5-pro",
15
+ "openai/gpt-5-mini"
16
+ ];
17
+
18
+ for (const model of models) {
19
+ console.log(`\n--- Using model: ${model} ---`);
20
+ const result = await summarize(model)(text);
21
+ console.log(result.output);
22
+ }
23
+ }
24
+
25
+ run();
@@ -0,0 +1,24 @@
1
+ import dotenv from "dotenv";
2
+ dotenv.config();
3
+ import { wrap } from "../../src/wrap";
4
+ import { OpenRouterModel } from "../../src/types/openrouter";
5
+
6
+ const translate = (model: OpenRouterModel) =>
7
+ wrap((text: string) => text, { task: "translate", provider: "openrouter", model });
8
+
9
+ async function run() {
10
+ const text = "Bonjour tout le monde!";
11
+
12
+ const models: OpenRouterModel[] = [
13
+ "openai/gpt-4.1",
14
+ "openai/gpt-5-chat"
15
+ ];
16
+
17
+ for (const model of models) {
18
+ console.log(`\n--- Using model: ${model} ---`);
19
+ const result = await translate(model)(text);
20
+ console.log(result.output);
21
+ }
22
+ }
23
+
24
+ run();
@@ -0,0 +1,19 @@
1
+ import dotenv from "dotenv";
2
+ dotenv.config();
3
+ import { wrap } from "../../src/wrap";
4
+
5
+ // Wrap the text function for translation to Urdu
6
+ const translate = wrap((text: string) => text, {
7
+ task: "translate",
8
+ provider: "openrouter",
9
+ targetLanguage: "Roman Urdu" // specify target language
10
+ });
11
+
12
+ async function run() {
13
+ const text = "Hello everyone! Welcome to OpenRouter demos.";
14
+
15
+ const result = await translate(text);
16
+ console.log(result.output);
17
+ }
18
+
19
+ run();
@@ -0,0 +1,25 @@
1
+ import dotenv from "dotenv";
2
+ dotenv.config();
3
+ import { wrap } from "../../src/wrap";
4
+ import { OpenRouterModel } from "../../src/types/openrouter";
5
+
6
+ const summarize = (model: OpenRouterModel) =>
7
+ wrap((text: string) => text, { task: "summarize", provider: "openrouter", model });
8
+
9
+ async function run() {
10
+ const text = "This is a demonstration of switching OpenRouter models to GPT-5 variants.";
11
+
12
+ const models: OpenRouterModel[] = [
13
+ "openai/gpt-5",
14
+ "openai/gpt-5-pro",
15
+ "openai/gpt-5-mini"
16
+ ];
17
+
18
+ for (const model of models) {
19
+ console.log(`\n--- Using model: ${model} ---`);
20
+ const result = await summarize(model)(text);
21
+ console.log(result.output);
22
+ }
23
+ }
24
+
25
+ run();
package/jest.config.js ADDED
@@ -0,0 +1,11 @@
1
+ const { createDefaultPreset } = require("ts-jest");
2
+
3
+ const tsJestTransformCfg = createDefaultPreset().transform;
4
+
5
+ /** @type {import("jest").Config} **/
6
+ module.exports = {
7
+ testEnvironment: "node",
8
+ transform: {
9
+ ...tsJestTransformCfg,
10
+ },
11
+ };
package/package.json CHANGED
@@ -1,19 +1,8 @@
1
1
  {
2
2
  "name": "npm-ai-hooks",
3
- "version": "1.0.0",
4
- "description": "Universal AI Hook Layer for Node.js – one wrapper for all AI providers.",
5
- "main": "dist/index.js",
6
- "module": "dist/index.js",
7
- "types": "dist/index.d.ts",
8
- "exports": {
9
- ".": {
10
- "import": "./dist/index.js",
11
- "require": "./dist/index.js"
12
- }
13
- },
14
- "files": [
15
- "dist/**/*"
16
- ],
3
+ "version": "1.0.1",
4
+ "description": "**Universal AI Hook Layer for Node.js – one wrapper for all AI providers.**\r Inject LLM-like behavior into any JavaScript or TypeScript function with a single line, without writing prompts, handling SDKs, or locking into any provider.",
5
+ "main": "index.js",
17
6
  "scripts": {
18
7
  "dev": "ts-node src/index.ts",
19
8
  "build:clean": "rimraf dist && tsc",
@@ -23,7 +12,7 @@
23
12
  "demo": "npx ts-node examples/demo.ts"
24
13
  },
25
14
  "keywords": [],
26
- "author": "AteebNoOne",
15
+ "author": "",
27
16
  "license": "ISC",
28
17
  "dependencies": {
29
18
  "axios": "^1.12.2",
@@ -40,4 +29,4 @@
40
29
  "ts-node": "^10.9.2",
41
30
  "typescript": "^5.9.3"
42
31
  }
43
- }
32
+ }
package/src/errors.ts ADDED
@@ -0,0 +1,23 @@
1
+ export class AIHookError extends Error {
2
+ code: string;
3
+ provider?: string;
4
+ suggestion?: string;
5
+
6
+ constructor(code: string, message: string, provider?: string, suggestion?: string) {
7
+ super(message);
8
+ this.code = code;
9
+ this.provider = provider;
10
+ this.suggestion = suggestion;
11
+ // Do NOT print pretty message in constructor
12
+ }
13
+
14
+ pretty() {
15
+ return `\n❌ AI-HOOK ERROR: ${this.message}` +
16
+ (this.provider ? `\n Provider: ${this.provider}` : "") +
17
+ (this.suggestion ? `\n Suggestion: ${this.suggestion}\n` : "");
18
+ }
19
+
20
+ toString() {
21
+ return this.pretty();
22
+ }
23
+ }
@@ -1 +1,2 @@
1
- export { wrap } from "./wrap";
1
+ // src/index.ts
2
+ export { wrap } from "./wrap";
@@ -0,0 +1,101 @@
1
+ import axios from "axios";
2
+ import { AIHookError } from "../errors";
3
+ import { ClaudeModel } from "../types/claude";
4
+
5
+ const BASE_URL = "https://api.anthropic.com/v1";
6
+ const ANTHROPIC_VERSION = "2023-06-01";
7
+
8
+ export async function callClaude(prompt: string, model: ClaudeModel): Promise<string> {
9
+ const apiKey = process.env.AI_HOOK_CLAUDE_KEY;
10
+ if (!apiKey) {
11
+ throw new AIHookError(
12
+ "INVALID_API_KEY",
13
+ "Missing Claude API key.",
14
+ "claude",
15
+ "Set AI_HOOK_CLAUDE_KEY in your environment variables."
16
+ );
17
+ }
18
+
19
+ try {
20
+ const response = await axios.post(
21
+ `${BASE_URL}/messages`,
22
+ {
23
+ model,
24
+ max_tokens: 4096,
25
+ messages: [
26
+ {
27
+ role: "user",
28
+ content: prompt
29
+ }
30
+ ]
31
+ },
32
+ {
33
+ headers: {
34
+ "x-api-key": apiKey,
35
+ "anthropic-version": ANTHROPIC_VERSION,
36
+ "Content-Type": "application/json"
37
+ }
38
+ }
39
+ );
40
+
41
+ const output = response.data?.content?.[0]?.text;
42
+ if (!output) {
43
+ throw new AIHookError(
44
+ "PROVIDER_ERROR",
45
+ "Claude returned empty response",
46
+ "claude",
47
+ "Check your model and API key"
48
+ );
49
+ }
50
+
51
+ return output;
52
+ } catch (err: any) {
53
+ if (err.response) {
54
+ const status = err.response.status;
55
+ const text = err.response.data?.error
56
+ ? JSON.stringify(err.response.data.error)
57
+ : err.response.statusText || "Unknown error";
58
+
59
+ if (status === 400)
60
+ throw new AIHookError(
61
+ "BAD_REQUEST",
62
+ `Claude rejected the request: ${text}`,
63
+ "claude",
64
+ "Check your prompt and model"
65
+ );
66
+ if (status === 401)
67
+ throw new AIHookError(
68
+ "INVALID_API_KEY",
69
+ `Invalid Claude API key: ${text}`,
70
+ "claude",
71
+ "Verify your AI_HOOK_CLAUDE_KEY environment variable"
72
+ );
73
+ if (status === 429)
74
+ throw new AIHookError(
75
+ "RATE_LIMIT",
76
+ `Too many requests to Claude: ${text}`,
77
+ "claude",
78
+ "Throttle requests or upgrade your plan"
79
+ );
80
+
81
+ throw new AIHookError(
82
+ "PROVIDER_ERROR",
83
+ `Claude API error: ${text}`,
84
+ "claude"
85
+ );
86
+ } else if (err.request) {
87
+ throw new AIHookError(
88
+ "NETWORK_ERROR",
89
+ "Network error while contacting Claude",
90
+ "claude",
91
+ "Check your internet connection"
92
+ );
93
+ } else {
94
+ throw new AIHookError(
95
+ "UNKNOWN_ERROR",
96
+ err.message,
97
+ "claude"
98
+ );
99
+ }
100
+ }
101
+ }