# Morpheus Node SDK

Official Node.js SDK for interacting with the Morpheus API Gateway at api.mor.org.

## Installation

```bash
npm install morpheus-node
# or
yarn add morpheus-node
# or
pnpm add morpheus-node
```

## Quick Start

```typescript
import MorpheusClient from 'morpheus-node';

// Initialize with API key
const client = new MorpheusClient({
  apiKey: 'your-api-key-here'
});

// Or initialize with email/password for authentication
const client = new MorpheusClient({
  baseURL: 'https://api.mor.org' // Optional, this is the default
});

// Login to get access token
const auth = await client.login({
  email: 'your-email@example.com',
  password: 'your-password'
});

// Create a chat completion
const response = await client.createChatCompletion({
  model: 'llama-3.3-70b',
  messages: [
    { role: 'system', content: 'You are a helpful assistant.' },
    { role: 'user', content: 'What is the capital of France?' }
  ]
});

console.log(response.choices[0].message.content);
```

## Configuration

```typescript
const client = new MorpheusClient({
  apiKey: 'your-api-key-here',      // Optional if using login
  accessToken: 'your-token',        // Optional, can be set after login
  baseURL: 'https://api.mor.org',   // Optional, this is the default
  timeout: 30000,                   // Optional, request timeout in ms
  maxRetries: 3,                    // Optional, max retry attempts
  retryDelay: 1000,                 // Optional, initial retry delay in ms
  headers: {                        // Optional, custom headers
    'X-Custom-Header': 'value'
  }
});
```

## API Reference

### Authentication

#### Register User

```typescript
const auth = await client.register({
  email: 'user@example.com',
  password: 'secure-password',
  username: 'optional-username'
});

// Access token is automatically stored for future requests
console.log(auth.access_token);
```

#### Login

```typescript
const auth = await client.login({
  email: 'user@example.com',
  password: 'your-password'
});

// Access token is automatically stored
console.log(auth.access_token);
```

#### Refresh Token

```typescript
const newAuth = await client.refreshToken({
  refresh_token: auth.refresh_token
});
```

#### API Keys Management

```typescript
// List API keys
const keys = await client.getApiKeys();

// Create new API key
const newKey = await client.createApiKey({
  name: 'My API Key'
});
console.log('New API Key:', newKey.key);

// Delete API key
await client.deleteApiKey(newKey.id);
```

#### Private Key Management

```typescript
// Store private key
await client.storePrivateKey({
  private_key: 'your-private-key'
});

// Check private key status
const status = await client.getPrivateKeyStatus();
console.log('Has private key:', status.has_private_key);

// Delete private key
await client.deletePrivateKey();
```

#### Delegation Management

```typescript
// Create delegation
const delegation = await client.storeDelegation({
  delegatee_address: '0x...',
  expires_at: '2024-12-31T23:59:59Z'
});

// List all delegations
const delegations = await client.getUserDelegations();

// Get active delegation
const active = await client.getActiveDelegation();

// Delete delegation
await client.deleteDelegation(delegation.id);
```

### Chat Completions

#### Create Chat Completion

```typescript
const response = await client.createChatCompletion({
  model: 'llama-3.3-70b',
  messages: [
    { role: 'user', content: 'Hello!' }
  ],
  temperature: 0.7,
  max_tokens: 1000
});

console.log(response.choices[0].message.content);
```

#### Stream Chat Completion

```typescript
const stream = client.streamChatCompletion({
  model: 'llama-3.3-70b',
  messages: [
    { role: 'user', content: 'Tell me a story' }
  ]
});

for await (const chunk of stream) {
  process.stdout.write(chunk.choices[0].delta?.content || '');
}
```

#### Stream with Event Emitter

```typescript
const events = client.streamChatCompletionEvents({
  model: 'llama-3.3-70b',
  messages: [
    { role: 'user', content: 'Tell me a story' }
  ]
});

events.on('message', (event) => {
  process.stdout.write(event.data.choices[0].delta?.content || '');
});

events.on('done', () => {
  console.log('\nStream complete!');
});

events.on('error', (event) => {
  console.error('Stream error:', event.error);
});
```

### Models

#### List Available Models

```typescript
const models = await client.listModels();
models.forEach(model => {
  console.log(`${model.id}: ${model.owned_by}`);
});
```

#### List All Models (Extended)

```typescript
const allModels = await client.listAllModels();
allModels.models.forEach(model => {
  console.log(`${model.id}: ${model.provider} (max context: ${model.max_context})`);
});
```

#### Get Rated Bids

```typescript
const bids = await client.getRatedBids();
bids.forEach(bid => {
  console.log(`${bid.model} by ${bid.provider}: ${bid.price_per_token} (rating: ${bid.rating})`);
});
```

### Sessions

#### Approve Spending

```typescript
await client.approveSpending({
  amount: '1000000000000000000', // 1 token in wei
  token: 'MOR' // Optional
});
```

#### Create Bid Session

```typescript
const session = await client.createBidSession({
  bid_id: 'bid-123',
  duration: 3600 // Optional, in seconds
});

console.log('Session ID:', session.session_id);
```

#### Create Model Session

```typescript
const session = await client.createModelSession({
  model: 'llama-3.3-70b',
  duration: 3600 // Optional
});
```

#### Session Management

```typescript
// Ping session to keep alive
await client.pingSession({
  session_id: session.session_id
});

// Close session
await client.closeSession({
  session_id: session.session_id
});
```

### Automation

#### Get Automation Settings

```typescript
const settings = await client.getAutomationSettings();
console.log('Automation enabled:', settings.enabled);
```

#### Update Automation Settings

```typescript
const updated = await client.updateAutomationSettings({
  enabled: true,
  max_spend_per_session: '1000000000000000000',
  max_spend_per_day: '10000000000000000000',
  allowed_models: ['llama-3.3-70b', 'gpt-4'],
  auto_approve_threshold: '100000000000000000'
});
```

### Health Check

```typescript
const health = await client.healthCheck();
console.log('API Status:', health.status);

// Root endpoint
const root = await client.getRoot();
```

## Error Handling

The SDK provides specific error types for different scenarios:

```typescript
import {
  MorpheusError,
  MorpheusAuthenticationError,
  MorpheusRateLimitError,
  MorpheusValidationError,
  MorpheusNetworkError,
  isMorpheusError
} from 'morpheus-node';

try {
  const response = await client.createChatCompletion({
    model: 'invalid-model',
    messages: []
  });
} catch (error) {
  if (isMorpheusError(error)) {
    console.error(`Morpheus Error: ${error.message}`);
    console.error(`Status: ${error.status}`);
    console.error(`Code: ${error.code}`);
    
    if (error instanceof MorpheusAuthenticationError) {
      // Handle authentication error - maybe refresh token
    } else if (error instanceof MorpheusRateLimitError) {
      // Handle rate limit
      console.log(`Retry after: ${error.retryAfter} seconds`);
    } else if (error instanceof MorpheusValidationError) {
      // Handle validation error
      console.log(`Invalid param: ${error.param}`);
    }
  }
}
```

## Advanced Usage

### Custom Request Options

```typescript
const response = await client.createChatCompletion(
  {
    model: 'llama-3.3-70b',
    messages: [{ role: 'user', content: 'Hello' }]
  },
  {
    timeout: 60000, // Override timeout for this request
    signal: abortController.signal, // AbortController for cancellation
    headers: {
      'X-Request-ID': 'custom-id'
    }
  }
);
```

### Token Management

```typescript
// Manually set access token
client.setAccessToken('new-access-token');

// Get current access token
const token = client.getAccessToken();
```

### Retry Configuration

The SDK automatically retries failed requests with exponential backoff:

- Network errors
- Timeout errors  
- 429 (Rate Limit) errors
- 502, 503, 504 (Server) errors

You can configure retry behavior:

```typescript
const client = new MorpheusClient({
  apiKey: 'your-api-key',
  maxRetries: 5,        // Maximum retry attempts
  retryDelay: 2000      // Initial delay between retries (doubles each time)
});
```

## TypeScript Support

This SDK is written in TypeScript and provides full type definitions:

```typescript
import type {
  ChatMessage,
  ChatCompletionRequest,
  ChatCompletionResponse,
  Model,
  SessionResponse,
  AutomationSettings,
  AuthResponse
} from 'morpheus-node';
```

## Requirements

- Node.js 14.0.0 or higher
- TypeScript 4.5+ (for TypeScript users)

## License

MIT

## Contributing

Contributions are welcome! Please feel free to submit a Pull Request.

## Support

For support, please visit [https://mor.org](https://mor.org) or open an issue on GitHub. 