langtrain 0.1.6 → 0.1.9

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "langtrain",
3
- "version": "0.1.6",
3
+ "version": "0.1.9",
4
4
  "description": "Unified JavaScript SDK for Langtrain Ecosystem",
5
5
  "main": "./dist/index.js",
6
6
  "module": "./dist/index.mjs",
@@ -37,7 +37,10 @@
37
37
  },
38
38
  "dependencies": {
39
39
  "@clack/prompts": "^1.0.1",
40
+ "axios": "^1.13.5",
40
41
  "commander": "^14.0.3",
42
+ "conf": "^15.1.0",
43
+ "gradient-string": "^3.0.0",
41
44
  "kleur": "^4.1.5"
42
45
  }
43
46
  }
package/src/agent.ts ADDED
@@ -0,0 +1,54 @@
1
+ import axios, { AxiosInstance } from 'axios';
2
+
3
+ export interface Agent {
4
+ id: string;
5
+ workspace_id: string;
6
+ name: string;
7
+ description?: string;
8
+ model_id?: string;
9
+ config: any;
10
+ is_active: boolean;
11
+ created_at: string;
12
+ updated_at: string;
13
+ }
14
+
15
+ export interface AgentRun {
16
+ id: string;
17
+ conversation_id: string;
18
+ success: boolean;
19
+ output?: any;
20
+ error?: string;
21
+ latency_ms: number;
22
+ tokens_used: number;
23
+ }
24
+
25
+ export class AgentClient {
26
+ private client: AxiosInstance;
27
+
28
+ constructor(private config: { apiKey: string, baseUrl?: string }) {
29
+ this.client = axios.create({
30
+ baseURL: config.baseUrl || 'https://api.langtrain.ai/api/v1',
31
+ headers: {
32
+ 'X-API-Key': config.apiKey,
33
+ 'Content-Type': 'application/json'
34
+ }
35
+ });
36
+ }
37
+
38
+ async list(workspaceId?: string): Promise<Agent[]> {
39
+ const params: any = {};
40
+ if (workspaceId) params.workspace_id = workspaceId;
41
+
42
+ const response = await this.client.get<{ agents: Agent[] }>('/agents', { params });
43
+ return response.data.agents;
44
+ }
45
+
46
+ async execute(agentId: string, input: any, messages: any[] = [], conversationId?: string): Promise<AgentRun> {
47
+ const response = await this.client.post<AgentRun>(`/agents/${agentId}/execute`, {
48
+ input,
49
+ messages,
50
+ conversation_id: conversationId
51
+ });
52
+ return response.data;
53
+ }
54
+ }
package/src/cli.ts CHANGED
@@ -1,12 +1,35 @@
1
1
  #!/usr/bin/env node
2
- import { intro, outro, select, text, spinner, isCancel, cancel } from '@clack/prompts';
3
- import { bgCyan, black, red, green } from 'kleur/colors';
2
+ import { intro, outro, select, text, spinner, isCancel, cancel, password } from '@clack/prompts';
3
+ import { bgCyan, black, red, green, yellow, gray } from 'kleur/colors';
4
4
  import { Command } from 'commander';
5
- import { Langvision, Langtune } from './index';
5
+ import { Langvision, Langtune, AgentClient, Agent } from './index';
6
+ import fs from 'fs';
7
+ import path from 'path';
8
+ import os from 'os';
9
+ import gradient from 'gradient-string';
6
10
 
7
- // Initialize clients
8
- const vision = new Langvision();
9
- const tune = new Langtune();
11
+ // Configuration
12
+ const CONFIG_DIR = path.join(os.homedir(), '.langtrain');
13
+ const CONFIG_FILE = path.join(CONFIG_DIR, 'config.json');
14
+
15
+ function getConfig() {
16
+ if (!fs.existsSync(CONFIG_FILE)) return {};
17
+ try {
18
+ return JSON.parse(fs.readFileSync(CONFIG_FILE, 'utf-8'));
19
+ } catch {
20
+ return {};
21
+ }
22
+ }
23
+
24
+ function saveConfig(config: any) {
25
+ if (!fs.existsSync(CONFIG_DIR)) {
26
+ fs.mkdirSync(CONFIG_DIR, { recursive: true });
27
+ }
28
+ fs.writeFileSync(CONFIG_FILE, JSON.stringify(config, null, 2));
29
+ }
30
+
31
+ // Initialize clients with config
32
+ // Clients are initialized inside main loop to support re-login
10
33
 
11
34
  async function main() {
12
35
  const program = new Command();
@@ -14,51 +37,179 @@ async function main() {
14
37
  program
15
38
  .name('langtrain')
16
39
  .description('Langtrain CLI for AI Model Fine-tuning and Generation')
17
- .version('0.1.5');
40
+ .version('0.1.9');
18
41
 
19
42
  program.action(async () => {
20
43
  console.clear();
21
- intro(`${bgCyan(black(' langtrain '))}`);
22
-
23
- const operation = await select({
24
- message: 'Select an operation:',
25
- options: [
26
- { value: 'tune-finetune', label: '🧠 Fine-tune Text Model (Langtune)' },
27
- { value: 'tune-generate', label: '📝 Generate Text (Langtune)' },
28
- { value: 'vision-finetune', label: '👁️ Fine-tune Vision Model (Langvision)' },
29
- { value: 'vision-generate', label: '🖼️ Generate Vision Response (Langvision)' },
30
- { value: 'exit', label: '🚪 Exit' }
31
- ],
32
- });
33
44
 
34
- if (isCancel(operation) || operation === 'exit') {
35
- outro('Goodbye!');
36
- process.exit(0);
37
- }
45
+ // Gradient Banner
46
+ const banner = `
47
+ ██╗ █████╗ ███╗ ██╗ ██████╗████████╗██████╗ █████╗ ██╗███╗ ██╗
48
+ ██║ ██╔══██╗████╗ ██║██╔════╝╚══██╔══╝██╔══██╗██╔══██╗██║████╗ ██║
49
+ ██║ ███████║██╔██╗ ██║██║ ███╗ ██║ ██████╔╝███████║██║██╔██╗ ██║
50
+ ██║ ██╔══██║██║╚██╗██║██║ ██║ ██║ ██╔══██╗██╔══██║██║██║╚██╗██║
51
+ ███████╗██║ ██║██║ ╚████║╚██████╔╝ ██║ ██║ ██║██║ ██║██║██║ ╚████║
52
+ ╚══════╝╚═╝ ╚═╝╚═╝ ╚═══╝ ╚═════╝ ╚═╝ ╚═╝ ╚═╝╚═╝ ╚═╝╚═╝╚═╝ ╚═══╝
53
+ `;
54
+ console.log(gradient(['#00DC82', '#36E4DA', '#0047E1'])(banner)); // Custom Langtrain Green-Cyan-Blue gradient
55
+ intro(`${bgCyan(black(' Langtrain SDK v0.1.9 '))}`);
38
56
 
39
- try {
40
- if (operation === 'tune-finetune') {
41
- await handleTuneFinetune();
42
- } else if (operation === 'tune-generate') {
43
- await handleTuneGenerate();
44
- } else if (operation === 'vision-finetune') {
45
- await handleVisionFinetune();
46
- } else if (operation === 'vision-generate') {
47
- await handleVisionGenerate();
57
+ // Check auth
58
+ const config = getConfig();
59
+ if (!config.apiKey) {
60
+ intro(yellow('Authentication required'));
61
+ const apiKey = await password({
62
+ message: 'Enter your Langtrain API Key:',
63
+ validate(value) {
64
+ if (!value || value.length === 0) return 'API Key is required';
65
+ },
66
+ });
67
+
68
+ if (isCancel(apiKey)) {
69
+ cancel('Operation cancelled');
70
+ process.exit(0);
48
71
  }
49
- } catch (error: any) {
50
- outro(red(`Error: ${error.message}`));
51
- process.exit(1);
72
+
73
+ saveConfig({ ...config, apiKey });
74
+ intro(green('Successfully logged in!'));
52
75
  }
53
76
 
54
- outro(green('Operation completed successfully!'));
77
+ while (true) {
78
+ const operation = await select({
79
+ message: 'Select an operation:',
80
+ options: [
81
+ { value: 'group-agents', label: '🤖 Agents (Server)', hint: 'Chat with custom agents' },
82
+ { value: 'agent-list', label: ' ↳ List & Run Agents' },
83
+
84
+ { value: 'group-tune', label: '🧠 Langtune (LLM)', hint: 'Fine-tuning & Text Generation' },
85
+ { value: 'tune-finetune', label: ' ↳ Fine-tune Text Model' },
86
+ { value: 'tune-generate', label: ' ↳ Generate Text' },
87
+
88
+ { value: 'group-vision', label: '👁️ Langvision (Vision)', hint: 'Vision Analysis & Tuning' },
89
+ { value: 'vision-finetune', label: ' ↳ Fine-tune Vision Model' },
90
+ { value: 'vision-generate', label: ' ↳ Generate Vision Response' },
91
+
92
+ { value: 'group-settings', label: '⚙️ Settings' },
93
+ { value: 'login', label: ' ↳ Update API Key' },
94
+ { value: 'exit', label: ' ↳ Exit' }
95
+ ],
96
+ });
97
+
98
+ if (isCancel(operation) || operation === 'exit') {
99
+ outro('Goodbye!');
100
+ process.exit(0);
101
+ }
102
+
103
+ if (typeof operation === 'string' && operation.startsWith('group-')) {
104
+ // Header selected, just loop
105
+ continue;
106
+ }
107
+
108
+ try {
109
+ // Re-read config & re-init clients
110
+ const currentConfig = getConfig();
111
+ const currentVision = new Langvision({ apiKey: currentConfig.apiKey });
112
+ const currentTune = new Langtune({ apiKey: currentConfig.apiKey });
113
+ const currentAgent = new AgentClient({ apiKey: currentConfig.apiKey, baseUrl: currentConfig.baseUrl });
114
+
115
+ if (operation === 'login') {
116
+ await handleLogin();
117
+ } else if (operation === 'tune-finetune') {
118
+ await handleTuneFinetune(currentTune);
119
+ } else if (operation === 'tune-generate') {
120
+ await handleTuneGenerate(currentTune);
121
+ } else if (operation === 'vision-finetune') {
122
+ await handleVisionFinetune(currentVision);
123
+ } else if (operation === 'vision-generate') {
124
+ await handleVisionGenerate(currentVision);
125
+ } else if (operation === 'agent-list') {
126
+ await handleAgentList(currentAgent);
127
+ }
128
+ } catch (error: any) {
129
+ outro(red(`Error: ${error.message}`));
130
+ // Don't exit, just loop back to menu
131
+ }
132
+ }
55
133
  });
56
134
 
57
135
  program.parse(process.argv);
58
136
  }
59
137
 
138
+ async function handleLogin() {
139
+ const apiKey = await password({
140
+ message: 'Enter your new Langtrain API Key:',
141
+ validate(value) {
142
+ if (!value || value.length === 0) return 'API Key is required';
143
+ },
144
+ });
145
+
146
+ if (isCancel(apiKey)) cancel('Operation cancelled');
147
+
148
+ const config = getConfig();
149
+ saveConfig({ ...config, apiKey });
150
+ intro(green('API Key updated successfully!'));
151
+ }
152
+
153
+ async function handleAgentList(client: AgentClient) {
154
+ const s = spinner();
155
+ s.start('Fetching agents...');
156
+ const agents = await client.list();
157
+ s.stop(`Found ${agents.length} agents`);
158
+
159
+ if (agents.length === 0) {
160
+ intro(yellow('No agents found in your workspace.'));
161
+ return;
162
+ }
163
+
164
+ const agentId = await select({
165
+ message: 'Select an agent to run:',
166
+ options: agents.map(a => ({ value: a.id, label: a.name, hint: a.description || 'No description' }))
167
+ });
168
+
169
+ if (isCancel(agentId)) return;
170
+
171
+ await handleAgentRun(client, agentId as string, agents.find(a => a.id === agentId)?.name || 'Agent');
172
+ }
173
+
174
+ async function handleAgentRun(client: AgentClient, agentId: string, agentName: string) {
175
+ intro(bgCyan(black(` Chatting with ${agentName} `)));
176
+ console.log(gray('Type "exit" to quit conversation.'));
177
+
178
+ let conversationId: string | undefined = undefined;
179
+
180
+ while (true) {
181
+ const input = await text({
182
+ message: 'You:',
183
+ placeholder: 'Type a message...',
184
+ });
185
+
186
+ if (isCancel(input) || input === 'exit') {
187
+ break;
188
+ }
189
+
190
+ const s = spinner();
191
+ s.start('Agent is thinking...');
192
+ try {
193
+ const result = await client.execute(agentId, { prompt: input }, [], conversationId);
194
+ s.stop();
195
+
196
+ if (result.output && result.output.response) {
197
+ console.log(gradient.pastel(`Agent: ${result.output.response}`));
198
+ } else {
199
+ console.log(gradient.pastel(`Agent: ${JSON.stringify(result.output)}`));
200
+ }
201
+
202
+ conversationId = result.conversation_id;
203
+ } catch (e: any) {
204
+ s.stop(red('Error running agent.'));
205
+ console.error(e);
206
+ }
207
+ }
208
+ }
209
+
210
+
60
211
  // Handler for Langtune Fine-tuning
61
- async function handleTuneFinetune() {
212
+ async function handleTuneFinetune(tune: Langtune) {
62
213
  const model = await text({
63
214
  message: 'Enter base model (e.g., gpt-3.5-turbo):',
64
215
  placeholder: 'gpt-3.5-turbo',
@@ -85,10 +236,12 @@ async function handleTuneFinetune() {
85
236
  if (isCancel(epochs)) cancel('Operation cancelled.');
86
237
 
87
238
  const s = spinner();
88
- s.start('Starting fine-tuning job...');
239
+ s.start('Connecting to Langtrain Cloud...');
240
+ await new Promise(r => setTimeout(r, 800)); // Simulatoin
241
+ s.message('Starting fine-tuning job...');
89
242
 
90
243
  try {
91
- // Check if FinetuneConfig types match what's needed.
244
+ // Check if FinetuneConfig types match what's needed.
92
245
  // Casting to any to bypass strict type checking for this demo or ensure types are imported correctly.
93
246
  // In a real scenario, we'd construct the full config object.
94
247
  const config: any = {
@@ -103,7 +256,7 @@ async function handleTuneFinetune() {
103
256
  };
104
257
 
105
258
  await tune.finetune(config);
106
- s.stop(green('Fine-tuning job started!'));
259
+ s.stop(green('Fine-tuning job started successfully! 🚀'));
107
260
  } catch (e: any) {
108
261
  s.stop(red('Failed to start job.'));
109
262
  throw e;
@@ -111,7 +264,7 @@ async function handleTuneFinetune() {
111
264
  }
112
265
 
113
266
  // Handler for Langtune Generation
114
- async function handleTuneGenerate() {
267
+ async function handleTuneGenerate(tune: Langtune) {
115
268
  const model = await text({
116
269
  message: 'Enter model path:',
117
270
  placeholder: './output/model',
@@ -126,13 +279,13 @@ async function handleTuneGenerate() {
126
279
  if (isCancel(prompt)) cancel('Operation cancelled');
127
280
 
128
281
  const s = spinner();
129
- s.start('Generating response...');
282
+ s.start('Connecting to Langtrain Inference API...');
130
283
 
131
284
  try {
132
285
  const response = await tune.generate(model as string, { prompt: prompt as string });
133
286
  s.stop('Generation complete');
134
287
  intro('Response:');
135
- console.log(response);
288
+ console.log(gradient.pastel(response));
136
289
  } catch (e: any) {
137
290
  s.stop(red('Generation failed.'));
138
291
  throw e;
@@ -140,7 +293,7 @@ async function handleTuneGenerate() {
140
293
  }
141
294
 
142
295
  // Handler for Langvision Fine-tuning
143
- async function handleVisionFinetune() {
296
+ async function handleVisionFinetune(vision: Langvision) {
144
297
  const model = await text({
145
298
  message: 'Enter base vision model:',
146
299
  placeholder: 'llava-v1.5-7b',
@@ -161,7 +314,9 @@ async function handleVisionFinetune() {
161
314
  });
162
315
 
163
316
  const s = spinner();
164
- s.start('Starting vision fine-tuning...');
317
+ s.start('Analyzing dataset structure...');
318
+ await new Promise(r => setTimeout(r, 800));
319
+ s.message('Starting vision fine-tuning on Langtrain Cloud...');
165
320
 
166
321
  try {
167
322
  const config: any = {
@@ -174,7 +329,7 @@ async function handleVisionFinetune() {
174
329
  outputDir: './vision-output'
175
330
  };
176
331
  await vision.finetune(config);
177
- s.stop(green('Vision fine-tuning started!'));
332
+ s.stop(green('Vision fine-tuning started successfully! 👁️'));
178
333
  } catch (e: any) {
179
334
  s.stop(red('Failed to start vision job.'));
180
335
  throw e;
@@ -182,7 +337,7 @@ async function handleVisionFinetune() {
182
337
  }
183
338
 
184
339
  // Handler for Langvision Generation
185
- async function handleVisionGenerate() {
340
+ async function handleVisionGenerate(vision: Langvision) {
186
341
  const model = await text({
187
342
  message: 'Enter model path:',
188
343
  placeholder: './vision-output/model',
@@ -197,13 +352,15 @@ async function handleVisionGenerate() {
197
352
  if (isCancel(prompt)) cancel('Operation cancelled');
198
353
 
199
354
  const s = spinner();
200
- s.start('Generating vision response...');
355
+ s.start('Uploading image and context...');
356
+ await new Promise(r => setTimeout(r, 600));
357
+ s.message('Generating vision response...');
201
358
 
202
359
  try {
203
360
  const response = await vision.generate(model as string, { prompt: prompt as string });
204
361
  s.stop('Generation complete');
205
362
  intro('Response:');
206
- console.log(response);
363
+ console.log(gradient.pastel(response));
207
364
  } catch (e: any) {
208
365
  s.stop(red('Generation failed.'));
209
366
  throw e;
package/src/index.ts CHANGED
@@ -3,8 +3,12 @@
3
3
  export { Langvision } from 'langvision';
4
4
  export { Langtune } from 'langtune';
5
5
 
6
+ // Export Agent Client
7
+ export { AgentClient, Agent, AgentRun } from './agent';
8
+
6
9
  // Export Types with Namespaces to avoid collisions
7
10
  import * as Vision from 'langvision';
8
11
  import * as Text from 'langtune';
12
+ import * as AgentTypes from './agent';
9
13
 
10
- export { Vision, Text };
14
+ export { Vision, Text, AgentTypes };