lynkr 4.0.0 → 4.2.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.
@@ -0,0 +1,571 @@
1
+ # Contributing Guide
2
+
3
+ Thank you for your interest in contributing to Lynkr! This guide will help you get started.
4
+
5
+ ---
6
+
7
+ ## Ways to Contribute
8
+
9
+ ### 1. Report Bugs
10
+
11
+ Found a bug? Please report it:
12
+
13
+ 1. **Search [existing issues](https://github.com/vishalveerareddy123/Lynkr/issues)** first
14
+ 2. **Create a new issue** with:
15
+ - Lynkr version
16
+ - Provider being used
17
+ - Steps to reproduce
18
+ - Expected vs actual behavior
19
+ - Error messages and logs
20
+ - Environment details (OS, Node version)
21
+
22
+ ### 2. Suggest Features
23
+
24
+ Have an idea for a new feature?
25
+
26
+ 1. **Search [GitHub Discussions](https://github.com/vishalveerareddy123/Lynkr/discussions)** first
27
+ 2. **Create a discussion** describing:
28
+ - The problem you're solving
29
+ - Proposed solution
30
+ - Use cases
31
+ - Alternatives considered
32
+
33
+ ### 3. Improve Documentation
34
+
35
+ Documentation improvements are always welcome:
36
+
37
+ - Fix typos or unclear wording
38
+ - Add examples
39
+ - Expand explanations
40
+ - Add troubleshooting steps
41
+ - Translate documentation
42
+
43
+ ### 4. Submit Code
44
+
45
+ Contributing code? Follow these steps:
46
+
47
+ 1. **Fork the repository**
48
+ 2. **Create a feature branch**: `git checkout -b feature/your-feature-name`
49
+ 3. **Make your changes**
50
+ 4. **Add tests** for new functionality
51
+ 5. **Run tests**: `npm test`
52
+ 6. **Commit with descriptive message**
53
+ 7. **Push to your fork**
54
+ 8. **Create a Pull Request**
55
+
56
+ ---
57
+
58
+ ## Development Setup
59
+
60
+ ### Prerequisites
61
+
62
+ - **Node.js 18+**
63
+ - **npm** or **yarn**
64
+ - **Git**
65
+ - Optional: **Docker** for testing containerized deployment
66
+
67
+ ### Clone and Install
68
+
69
+ ```bash
70
+ # Fork the repo on GitHub first, then:
71
+ git clone https://github.com/YOUR_USERNAME/Lynkr.git
72
+ cd Lynkr
73
+
74
+ # Install dependencies
75
+ npm install
76
+
77
+ # Copy environment template
78
+ cp .env.example .env
79
+
80
+ # Edit .env with your test credentials
81
+ nano .env
82
+ ```
83
+
84
+ ### Run in Development Mode
85
+
86
+ ```bash
87
+ # Auto-restart on file changes
88
+ npm run dev
89
+
90
+ # Or normal mode
91
+ npm start
92
+ ```
93
+
94
+ ### Run Tests
95
+
96
+ ```bash
97
+ # Run all tests
98
+ npm test
99
+
100
+ # Run specific test file
101
+ npm test test/config.test.js
102
+
103
+ # Run with coverage
104
+ npm run test:coverage
105
+ ```
106
+
107
+ ---
108
+
109
+ ## Code Style
110
+
111
+ ### General Guidelines
112
+
113
+ - **Use modern JavaScript** (ES6+)
114
+ - **Follow existing code style** (2-space indentation)
115
+ - **Add comments** for complex logic
116
+ - **Write descriptive variable names**
117
+ - **Keep functions small and focused**
118
+
119
+ ### File Organization
120
+
121
+ ```
122
+ src/
123
+ ├── api/ # Express routes and middleware
124
+ ├── clients/ # Provider client implementations
125
+ ├── config/ # Configuration loading and validation
126
+ ├── orchestrator/ # Agent loop and tool execution
127
+ ├── tools/ # Tool implementations
128
+ ├── cache/ # Caching layer
129
+ ├── observability/ # Metrics and logging
130
+ ├── db/ # Database operations
131
+ └── mcp/ # Model Context Protocol integration
132
+ ```
133
+
134
+ ### Naming Conventions
135
+
136
+ - **Files**: `kebab-case.js` (e.g., `prompt-cache.js`)
137
+ - **Functions**: `camelCase` (e.g., `invokeModel()`)
138
+ - **Constants**: `UPPER_SNAKE_CASE` (e.g., `DEFAULT_PORT`)
139
+ - **Classes**: `PascalCase` (e.g., `CircuitBreaker`)
140
+
141
+ ---
142
+
143
+ ## Testing Guidelines
144
+
145
+ ### Writing Tests
146
+
147
+ Use Node.js built-in test runner:
148
+
149
+ ```javascript
150
+ const assert = require("assert");
151
+ const { describe, it, beforeEach, afterEach } = require("node:test");
152
+
153
+ describe("Feature name", () => {
154
+ beforeEach(() => {
155
+ // Setup
156
+ });
157
+
158
+ afterEach(() => {
159
+ // Cleanup
160
+ });
161
+
162
+ it("should do something specific", () => {
163
+ // Arrange
164
+ const input = "test";
165
+
166
+ // Act
167
+ const result = myFunction(input);
168
+
169
+ // Assert
170
+ assert.strictEqual(result, "expected");
171
+ });
172
+ });
173
+ ```
174
+
175
+ ### Test Coverage
176
+
177
+ - **Aim for 80%+ coverage** for new code
178
+ - **Test edge cases** and error conditions
179
+ - **Mock external dependencies** (API calls, file system)
180
+ - **Test happy paths** and failure scenarios
181
+
182
+ ### Running Tests
183
+
184
+ ```bash
185
+ # All tests
186
+ npm test
187
+
188
+ # Specific file
189
+ npm test test/config.test.js
190
+
191
+ # With coverage
192
+ npm run test:coverage
193
+
194
+ # Watch mode (runs on file changes)
195
+ npm run test:watch
196
+ ```
197
+
198
+ ---
199
+
200
+ ## Pull Request Process
201
+
202
+ ### Before Submitting
203
+
204
+ 1. **Ensure tests pass**: `npm test`
205
+ 2. **Check code style**: Follow existing conventions
206
+ 3. **Update documentation** if needed
207
+ 4. **Add test coverage** for new features
208
+ 5. **Rebase on latest main**: `git rebase origin/main`
209
+
210
+ ### PR Template
211
+
212
+ When creating a pull request, include:
213
+
214
+ ```markdown
215
+ ## Description
216
+ Brief description of the changes
217
+
218
+ ## Type of Change
219
+ - [ ] Bug fix
220
+ - [ ] New feature
221
+ - [ ] Documentation update
222
+ - [ ] Performance improvement
223
+ - [ ] Refactoring
224
+
225
+ ## Changes Made
226
+ - Item 1
227
+ - Item 2
228
+
229
+ ## Testing
230
+ - [ ] Existing tests pass
231
+ - [ ] Added new tests
232
+ - [ ] Manual testing performed
233
+
234
+ ## Checklist
235
+ - [ ] Code follows style guidelines
236
+ - [ ] Documentation updated
237
+ - [ ] Tests added/updated
238
+ - [ ] No new warnings
239
+ ```
240
+
241
+ ### Review Process
242
+
243
+ 1. **Maintainers will review** your PR
244
+ 2. **Address feedback** if requested
245
+ 3. **Make changes** in new commits
246
+ 4. **Once approved**, maintainer will merge
247
+
248
+ ---
249
+
250
+ ## Adding a New Provider
251
+
252
+ To add support for a new LLM provider:
253
+
254
+ ### 1. Update Configuration
255
+
256
+ **File**: `src/config/index.js`
257
+
258
+ ```javascript
259
+ // Add to SUPPORTED_MODEL_PROVIDERS
260
+ const SUPPORTED_MODEL_PROVIDERS = new Set([
261
+ "databricks", "azure-anthropic", "ollama",
262
+ "openrouter", "azure-openai", "openai",
263
+ "llamacpp", "lmstudio", "bedrock", "newprovider" // Add here
264
+ ]);
265
+
266
+ // Parse environment variables
267
+ const newProviderApiKey = process.env.NEW_PROVIDER_API_KEY?.trim() || null;
268
+ const newProviderEndpoint = process.env.NEW_PROVIDER_ENDPOINT?.trim() || "https://api.newprovider.com";
269
+
270
+ // Add validation
271
+ if (modelProvider === "newprovider" && !newProviderApiKey) {
272
+ throw new Error("NEW_PROVIDER_API_KEY is required when MODEL_PROVIDER=newprovider");
273
+ }
274
+
275
+ // Export config
276
+ module.exports = {
277
+ // ...
278
+ newProvider: {
279
+ apiKey: newProviderApiKey,
280
+ endpoint: newProviderEndpoint,
281
+ },
282
+ };
283
+ ```
284
+
285
+ ### 2. Implement Invocation Function
286
+
287
+ **File**: `src/clients/databricks.js`
288
+
289
+ ```javascript
290
+ /**
291
+ * Invoke new provider
292
+ * @param {Object} body - Anthropic-format request body
293
+ * @returns {Object} Response with json and actualProvider
294
+ */
295
+ async function invokeNewProvider(body) {
296
+ // 1. Validate configuration
297
+ if (!config.newProvider?.apiKey) {
298
+ throw new Error("NEW_PROVIDER_API_KEY is required");
299
+ }
300
+
301
+ // 2. Convert Anthropic format to provider format
302
+ const providerRequest = convertAnthropicToNewProviderFormat(body);
303
+
304
+ // 3. Make API request
305
+ const response = await fetch(config.newProvider.endpoint, {
306
+ method: "POST",
307
+ headers: {
308
+ "Content-Type": "application/json",
309
+ "Authorization": `Bearer ${config.newProvider.apiKey}`,
310
+ },
311
+ body: JSON.stringify(providerRequest),
312
+ });
313
+
314
+ if (!response.ok) {
315
+ throw new Error(`Provider API error: ${response.statusText}`);
316
+ }
317
+
318
+ const data = await response.json();
319
+
320
+ // 4. Convert provider format back to Anthropic format
321
+ const anthropicResponse = convertNewProviderToAnthropicFormat(data);
322
+
323
+ return {
324
+ json: anthropicResponse,
325
+ actualProvider: "newprovider",
326
+ };
327
+ }
328
+
329
+ // Add to invokeModel switch
330
+ async function invokeModel(body, initialProvider) {
331
+ // ...
332
+ } else if (initialProvider === "newprovider") {
333
+ return await invokeNewProvider(body);
334
+ }
335
+ // ...
336
+ }
337
+ ```
338
+
339
+ ### 3. Add Format Conversion
340
+
341
+ Create `src/clients/newprovider-utils.js`:
342
+
343
+ ```javascript
344
+ /**
345
+ * Convert Anthropic format to provider format
346
+ */
347
+ function convertAnthropicToNewProviderFormat(body) {
348
+ return {
349
+ messages: convertMessages(body.messages),
350
+ max_tokens: body.max_tokens || 4096,
351
+ temperature: body.temperature || 0.7,
352
+ // ... provider-specific fields
353
+ };
354
+ }
355
+
356
+ /**
357
+ * Convert provider format to Anthropic format
358
+ */
359
+ function convertNewProviderToAnthropicFormat(response) {
360
+ return {
361
+ id: response.id || `msg_${Date.now()}`,
362
+ type: "message",
363
+ role: "assistant",
364
+ content: [
365
+ {
366
+ type: "text",
367
+ text: response.output || response.message || "",
368
+ },
369
+ ],
370
+ model: response.model,
371
+ stop_reason: "end_turn",
372
+ usage: {
373
+ input_tokens: response.usage?.input || 0,
374
+ output_tokens: response.usage?.output || 0,
375
+ },
376
+ };
377
+ }
378
+
379
+ module.exports = {
380
+ convertAnthropicToNewProviderFormat,
381
+ convertNewProviderToAnthropicFormat,
382
+ };
383
+ ```
384
+
385
+ ### 4. Add Tests
386
+
387
+ **File**: `test/newprovider-integration.test.js`
388
+
389
+ ```javascript
390
+ const assert = require("assert");
391
+ const { describe, it, beforeEach, afterEach } = require("node:test");
392
+
393
+ describe("New Provider Integration", () => {
394
+ let originalEnv;
395
+
396
+ beforeEach(() => {
397
+ originalEnv = { ...process.env };
398
+ delete require.cache[require.resolve("../src/config")];
399
+ });
400
+
401
+ afterEach(() => {
402
+ process.env = originalEnv;
403
+ });
404
+
405
+ it("should accept newprovider as MODEL_PROVIDER", () => {
406
+ process.env.MODEL_PROVIDER = "newprovider";
407
+ process.env.NEW_PROVIDER_API_KEY = "test-key";
408
+
409
+ const config = require("../src/config");
410
+ assert.strictEqual(config.modelProvider.type, "newprovider");
411
+ });
412
+
413
+ it("should throw error when API key is missing", () => {
414
+ process.env.MODEL_PROVIDER = "newprovider";
415
+ delete process.env.NEW_PROVIDER_API_KEY;
416
+
417
+ assert.throws(
418
+ () => require("../src/config"),
419
+ /NEW_PROVIDER_API_KEY is required/
420
+ );
421
+ });
422
+
423
+ // Add more tests...
424
+ });
425
+ ```
426
+
427
+ ### 5. Update Documentation
428
+
429
+ - Add provider to `documentation/providers.md`
430
+ - Add configuration example to `.env.example`
431
+ - Update README.md provider table
432
+ - Add quick start example
433
+
434
+ ---
435
+
436
+ ## Adding a New Tool
437
+
438
+ To add a new tool implementation:
439
+
440
+ ### 1. Create Tool File
441
+
442
+ **File**: `src/tools/your-tool.js`
443
+
444
+ ```javascript
445
+ const logger = require("../logger");
446
+
447
+ /**
448
+ * Tool implementation
449
+ * @param {Object} input - Tool input parameters
450
+ * @param {Object} context - Execution context
451
+ * @returns {Object} Tool result
452
+ */
453
+ async function yourTool(input, context) {
454
+ try {
455
+ // Validate input
456
+ if (!input.requiredParam) {
457
+ throw new Error("requiredParam is required");
458
+ }
459
+
460
+ // Execute tool logic
461
+ const result = await doSomething(input.requiredParam);
462
+
463
+ // Return result
464
+ return {
465
+ success: true,
466
+ data: result,
467
+ };
468
+ } catch (error) {
469
+ logger.error({ error, input }, "Tool execution failed");
470
+ throw error;
471
+ }
472
+ }
473
+
474
+ module.exports = {
475
+ yourTool,
476
+ };
477
+ ```
478
+
479
+ ### 2. Register Tool
480
+
481
+ **File**: `src/tools/index.js`
482
+
483
+ ```javascript
484
+ const { yourTool } = require("./your-tool");
485
+
486
+ const STANDARD_TOOLS = [
487
+ // ... existing tools
488
+ {
489
+ name: "your_tool",
490
+ description: "Description of what your tool does",
491
+ input_schema: {
492
+ type: "object",
493
+ properties: {
494
+ requiredParam: {
495
+ type: "string",
496
+ description: "Description of parameter",
497
+ },
498
+ },
499
+ required: ["requiredParam"],
500
+ },
501
+ },
502
+ ];
503
+
504
+ // Add to tool execution mapping
505
+ async function executeTool(toolName, toolInput, context) {
506
+ switch (toolName) {
507
+ // ... existing cases
508
+ case "your_tool":
509
+ return await yourTool(toolInput, context);
510
+ default:
511
+ throw new Error(`Unknown tool: ${toolName}`);
512
+ }
513
+ }
514
+ ```
515
+
516
+ ### 3. Add Tests
517
+
518
+ **File**: `test/tools/your-tool.test.js`
519
+
520
+ ```javascript
521
+ const assert = require("assert");
522
+ const { describe, it } = require("node:test");
523
+ const { yourTool } = require("../../src/tools/your-tool");
524
+
525
+ describe("Your Tool", () => {
526
+ it("should execute successfully with valid input", async () => {
527
+ const result = await yourTool(
528
+ { requiredParam: "test" },
529
+ { workspaceRoot: "/tmp" }
530
+ );
531
+
532
+ assert.strictEqual(result.success, true);
533
+ assert.ok(result.data);
534
+ });
535
+
536
+ it("should throw error with invalid input", async () => {
537
+ await assert.rejects(
538
+ () => yourTool({}, {}),
539
+ /requiredParam is required/
540
+ );
541
+ });
542
+ });
543
+ ```
544
+
545
+ ---
546
+
547
+ ## Community Guidelines
548
+
549
+ ### Code of Conduct
550
+
551
+ - **Be respectful** and inclusive
552
+ - **Welcome newcomers** and help them contribute
553
+ - **Provide constructive feedback**
554
+ - **Focus on the code**, not the person
555
+ - **Assume good intentions**
556
+
557
+ ### Getting Help
558
+
559
+ - **[GitHub Discussions](https://github.com/vishalveerareddy123/Lynkr/discussions)** - Ask questions
560
+ - **[Discord](https://discord.gg/qF7DDxrX)** - Real-time chat
561
+ - **[Issues](https://github.com/vishalveerareddy123/Lynkr/issues)** - Report bugs
562
+
563
+ ---
564
+
565
+ ## License
566
+
567
+ By contributing to Lynkr, you agree that your contributions will be licensed under the Apache 2.0 License.
568
+
569
+ ---
570
+
571
+ Thank you for contributing to Lynkr! 🎉