Typescript SDK Guide

Axicov TypeScript SDK

Introduction

The Axicov TypeScript SDK provides a robust interface for integrating with the Axicov platform, allowing developers to easily incorporate computer vision capabilities into their applications. This SDK handles authentication, request formatting, and response parsing, enabling you to focus on building your application.

Note: The Axicov SDK requires TypeScript 4.5+ and Node.js 16.0+

Installation

Install the Axicov SDK using your preferred package manager:

Using npm:

npm install @axicov/sdk

Using yarn:

yarn add @axicov/sdk

Using pnpm:

pnpm add @axicov/sdk

Authentication

The Axicov SDK uses API keys for authentication. You can obtain an API key from the Axicov Developer Console.

Warning: Protect your API keys carefully. Never expose them in client-side code or commit them to your repository.

Quick Start

import { AxicovSDK } from '@axicov/sdk';

// Initialize the SDK with your API key
const axicov = new AxicovSDK({
  apiKey: 'your-api-key-here',
  region: 'us-east-1' // Optional: defaults to closest region
});

// Example: Detect objects in an image
async function detectObjects(imageUrl: string) {
  try {
    const result = await axicov.vision.detectObjects({
      image: { url: imageUrl },
      confidence: 0.7 // Minimum confidence threshold (0-1)
    });
    
    console.log(`Detected ${result.objects.length} objects`);
    result.objects.forEach(obj => {
      console.log(`${obj.label} (${obj.confidence.toFixed(2)}): [${obj.boundingBox.x}, ${obj.boundingBox.y}, ${obj.boundingBox.width}, ${obj.boundingBox.height}]`);
    });
    
    return result;
  } catch (error) {
    console.error('Error detecting objects:', error);
    throw error;
  }
}

SDK Configuration

The AxicovSDK constructor accepts the following configuration options:

Option
Type
Default
Description

apiKey

string

Required

Your Axicov API key

region

string

Auto-detected

API region (e.g., 'us-east-1', 'eu-west-1')

version

string

'v1'

API version to use

timeout

number

60000

Request timeout in milliseconds

maxRetries

number

3

Maximum retry attempts for failed requests

retryDelay

number

1000

Base delay between retries in milliseconds

customEndpoint

string

null

Custom API endpoint URL (for enterprise customers)

logLevel

'debug' | 'info' | 'warn' | 'error' | 'none'

'warn'

Logging verbosity

Example with custom configuration:

const axicov = new AxicovSDK({
  apiKey: process.env.AXICOV_API_KEY,
  region: 'eu-west-1',
  timeout: 120000,
  maxRetries: 5,
  logLevel: 'debug'
});

Core Services

The Axicov SDK is organized into specialized services:

  • vision: Image analysis and object detection

  • document: Document parsing and data extraction

  • video: Video analysis and content detection

  • custom: Custom model training and inference

  • batch: Batch processing for high-volume operations

  • management: Model and project management

Vision Service

The Vision service provides computer vision capabilities for analyzing images.

Object Detection

Detect and locate objects within an image:

const result = await axicov.vision.detectObjects({
  image: { 
    url: 'https://example.com/image.jpg'
    // OR use a buffer
    // buffer: imageBuffer,
    // mimeType: 'image/jpeg'
  },
  confidence: 0.6, // Minimum confidence threshold (0-1)
  maxResults: 50, // Maximum number of results to return
  models: ['general-v2'], // Optional: specific model to use
  customLabels: ['product-skus'] // Optional: reference custom trained labels
});

// Process detected objects
result.objects.forEach(obj => {
  console.log(`${obj.label}: ${obj.confidence.toFixed(2)}`);
  console.log(`Position: ${JSON.stringify(obj.boundingBox)}`);
});

Facial Analysis

Analyze faces within an image:

const result = await axicov.vision.analyzeFaces({
  image: { url: 'https://example.com/people.jpg' },
  attributes: ['age', 'gender', 'emotion', 'landmarks'], // Requested attributes
  maxFaces: 10 // Maximum number of faces to analyze
});

// Process detected faces
result.faces.forEach(face => {
  console.log(`Face #${face.id}`);
  console.log(`- Age range: ${face.age.low}-${face.age.high} (confidence: ${face.age.confidence.toFixed(2)})`);
  console.log(`- Gender: ${face.gender.value} (confidence: ${face.gender.confidence.toFixed(2)})`);
  console.log(`- Emotion: ${face.emotion.primary} (confidence: ${face.emotion.confidence.toFixed(2)})`);
  console.log(`- Position: ${JSON.stringify(face.boundingBox)}`);
});

Image Classification

Classify the content of an image:

const result = await axicov.vision.classifyImage({
  image: { url: 'https://example.com/scene.jpg' },
  maxLabels: 10,
  confidence: 0.7,
  hierarchical: true // Return hierarchical categories
});

// Process classifications
result.labels.forEach(label => {
  console.log(`${label.name}: ${label.confidence.toFixed(2)}`);
  
  // Process hierarchical categories if enabled
  if (label.hierarchy && label.hierarchy.length) {
    console.log(`  Hierarchy: ${label.hierarchy.join(' > ')}`);
  }
});

Image Moderation

Detect inappropriate content in images:

const result = await axicov.vision.moderateContent({
  image: { url: 'https://example.com/user-content.jpg' },
  categories: ['explicit', 'violence', 'drugs', 'hate'], // Content categories to check
  confidence: 0.6
});

// Process moderation results
if (result.flagged) {
  console.log('Image was flagged for the following reasons:');
  result.categories.forEach(category => {
    if (category.flagged) {
      console.log(`- ${category.name}: ${category.confidence.toFixed(2)}`);
    }
  });
} else {
  console.log('Image passed content moderation');
}

Text Recognition (OCR)

Extract text from images:

const result = await axicov.vision.recognizeText({
  image: { url: 'https://example.com/document.jpg' },
  type: 'all', // 'all', 'handwritten', or 'printed'
  language: 'auto', // Language code or 'auto' for automatic detection
  enhanceResults: true // Apply additional processing to improve results
});

// Process recognized text
console.log(`Detected text (confidence: ${result.confidence.toFixed(2)})`);
console.log(`Detected language: ${result.language}`);

// All text content
console.log('Full text:');
console.log(result.text);

// Process individual text blocks with position information
result.blocks.forEach(block => {
  console.log(`Block: ${block.text}`);
  console.log(`Position: ${JSON.stringify(block.boundingBox)}`);
  console.log(`Confidence: ${block.confidence.toFixed(2)}`);
});

Document Service

The Document service provides capabilities for parsing and extracting data from various document formats.

Document Parsing

Extract structured data from documents:

const result = await axicov.document.parse({
  document: { url: 'https://example.com/invoice.pdf' },
  type: 'invoice', // Document type (invoice, receipt, id, etc.)
  extractTables: true, // Extract tabular data
  ocrIfNeeded: true // Apply OCR if document contains image-based text
});

// Access extracted key-value pairs
Object.entries(result.fields).forEach(([key, value]) => {
  console.log(`${key}: ${value.text} (confidence: ${value.confidence.toFixed(2)})`);
});

// Access extracted tables
result.tables.forEach((table, index) => {
  console.log(`Table ${index + 1}:`);
  table.forEach(row => console.log(row.join(', ')));
});

Custom Document Extraction

Extract data using custom document templates:

const result = await axicov.document.extractWithTemplate({
  document: { url: 'https://example.com/custom-form.pdf' },
  templateId: 'template_12345', // ID of your custom template
  version: 1 // Optional: template version to use
});

// Access extracted fields based on template
console.log(`Extraction completed with confidence: ${result.confidence.toFixed(2)}`);
Object.entries(result.fields).forEach(([key, value]) => {
  console.log(`${key}: ${value.text} (confidence: ${value.confidence.toFixed(2)})`);
});

Form Extraction

Extract structured data from forms:

const result = await axicov.document.extractForm({
  document: { url: 'https://example.com/form.pdf' },
  language: 'en' // Optional: specify form language
});

// Access extracted form fields
result.fields.forEach(field => {
  console.log(`${field.name}: ${field.value}`);
  console.log(`Type: ${field.type}`); // checkbox, text, signature, etc.
  console.log(`Position: ${JSON.stringify(field.boundingBox)}`);
});

Video Service

The Video service provides capabilities for analyzing video content.

Video Object Tracking

Track objects across video frames:

// Start video analysis job
const job = await axicov.video.startObjectTracking({
  video: { url: 'https://example.com/video.mp4' },
  confidence: 0.7,
  frameRate: 1, // Analyze 1 frame per second
  objectLabels: ['person', 'vehicle', 'animal'], // Optional: limit to specific objects
  notifications: {
    onComplete: {
      type: 'webhook',
      url: 'https://your-server.com/webhooks/video-processing'
    }
  }
});

console.log(`Job started with ID: ${job.id}`);

// Check job status (if not using webhooks)
const status = await axicov.video.getJobStatus(job.id);
console.log(`Job status: ${status.state}`); // 'in-progress', 'completed', 'failed'

// Get job results when complete
if (status.state === 'completed') {
  const results = await axicov.video.getObjectTrackingResults(job.id);
  
  // Process tracking results
  results.tracks.forEach(track => {
    console.log(`Object ID: ${track.id}`);
    console.log(`Label: ${track.label}`);
    console.log(`Confidence: ${track.confidence.toFixed(2)}`);
    console.log(`Appears in ${track.timestamps.length} frames`);
    
    // Sample appearance timestamps and positions
    track.timestamps.forEach((timestamp, i) => {
      console.log(`  At ${timestamp}s: ${JSON.stringify(track.boundingBoxes[i])}`);
    });
  });
}

Video Content Moderation

Analyze video content for inappropriate material:

// Start video moderation job
const job = await axicov.video.startContentModeration({
  video: { url: 'https://example.com/user-video.mp4' },
  categories: ['explicit', 'violence', 'profanity'],
  minConfidence: 0.6,
  sampleRate: 1 // Frames per second to analyze
});

// Wait for completion (alternatively, use webhooks)
let status;
do {
  await new Promise(resolve => setTimeout(resolve, 5000)); // Wait 5 seconds between checks
  status = await axicov.video.getJobStatus(job.id);
  console.log(`Moderation job status: ${status.state}`);
} while (status.state === 'in-progress');

if (status.state === 'completed') {
  const results = await axicov.video.getModerationResults(job.id);
  
  if (results.flagged) {
    console.log('Video was flagged for the following reasons:');
    
    // Flagged segments by timestamp
    results.segments.forEach(segment => {
      if (segment.flagged) {
        console.log(`- From ${segment.startTime}s to ${segment.endTime}s:`);
        segment.categories.forEach(category => {
          if (category.flagged) {
            console.log(`  - ${category.name}: ${category.confidence.toFixed(2)}`);
          }
        });
      }
    });
  } else {
    console.log('Video passed content moderation');
  }
}

Custom Service

The Custom service allows you to train and use custom computer vision models.

Training a Custom Model

Train a custom object detection model:

// Create a new project
const project = await axicov.custom.createProject({
  name: 'Product Detection',
  description: 'Detect our retail products in images',
  type: 'object-detection' // 'object-detection', 'classification', or 'segmentation'
});

console.log(`Created project with ID: ${project.id}`);

// Upload training data
const dataset = await axicov.custom.createDataset({
  projectId: project.id,
  name: 'Retail Products v1'
});

// Upload labeled training images (assumes you have a prepared dataset)
const uploadResult = await axicov.custom.uploadTrainingData({
  datasetId: dataset.id,
  format: 'coco', // 'coco', 'pascal-voc', or 'axicov-json'
  data: trainingDataBuffer, // Your formatted training data
  validateOnly: false // Set to true to validate without uploading
});

console.log(`Uploaded ${uploadResult.imagesCount} images with ${uploadResult.annotationsCount} annotations`);

// Start model training
const training = await axicov.custom.startTraining({
  projectId: project.id,
  datasetId: dataset.id,
  name: 'Model v1',
  configuration: {
    augmentation: true, // Apply data augmentation
    baseModel: 'efficient-det-d1', // Optional: select base model
    maxEpochs: 100,
    earlyStoppingPatience: 10,
    batchSize: 16
  }
});

console.log(`Training started with ID: ${training.id}`);

// Check training status
const trainingStatus = await axicov.custom.getTrainingStatus(training.id);
console.log(`Training status: ${trainingStatus.status}`);
console.log(`Progress: ${trainingStatus.progress.toFixed(2)}%`);
console.log(`Current metrics: ${JSON.stringify(trainingStatus.metrics)}`);

Using a Custom Model

Use your trained custom model for inference:

// Detect objects using custom model
const result = await axicov.custom.detect({
  modelId: 'model_12345',
  image: { url: 'https://example.com/test-image.jpg' },
  version: 1, // Optional: model version
  confidence: 0.5,
  maxResults: 50
});

// Process detected objects
console.log(`Detected ${result.objects.length} objects`);
result.objects.forEach(obj => {
  console.log(`${obj.label}: ${obj.confidence.toFixed(2)}`);
  console.log(`Position: ${JSON.stringify(obj.boundingBox)}`);
});

Batch Service

The Batch service allows processing large volumes of images or documents asynchronously.

Creating a Batch Job

Create and monitor a batch processing job:

// Create a batch job for object detection on multiple images
const job = await axicov.batch.createJob({
  type: 'vision.detectObjects',
  inputs: [
    { url: 'https://example.com/image1.jpg' },
    { url: 'https://example.com/image2.jpg' },
    { url: 'https://example.com/image3.jpg' },
    // Add more images as needed
  ],
  parameters: {
    confidence: 0.7,
    maxResults: 50
  },
  resultConfig: {
    outputFormat: 'json',
    destination: {
      type: 's3',
      bucket: 'your-bucket',
      prefix: 'batch-results/'
    }
  },
  notifications: {
    onComplete: {
      type: 'webhook',
      url: 'https://your-server.com/webhooks/batch-complete'
    },
    onError: {
      type: 'webhook',
      url: 'https://your-server.com/webhooks/batch-error'
    }
  }
});

console.log(`Batch job created with ID: ${job.id}`);

// Check job status
const status = await axicov.batch.getJobStatus(job.id);
console.log(`Job status: ${status.status}`);
console.log(`Progress: ${status.progress.completed}/${status.progress.total} (${status.progress.failed} failed)`);

// Get job results when complete (if not using S3 destination)
if (status.status === 'completed') {
  const results = await axicov.batch.getJobResults(job.id);
  
  // Process results
  results.forEach((result, index) => {
    if (result.status === 'succeeded') {
      console.log(`Item ${index + 1} processed successfully`);
      console.log(`Detected ${result.data.objects.length} objects`);
    } else {
      console.log(`Item ${index + 1} failed: ${result.error.message}`);
    }
  });
}

Error Handling

The Axicov SDK provides structured error handling for different types of failures:

import { 
  AxicovError, 
  AuthenticationError, 
  ValidationError, 
  RateLimitError,
  ResourceNotFoundError,
  ServiceError,
  NetworkError
} from '@axicov/sdk';

try {
  const result = await axicov.vision.detectObjects({
    image: { url: 'https://example.com/image.jpg' }
  });
} catch (error) {
  if (error instanceof AuthenticationError) {
    console.error('Authentication failed:', error.message);
    // Handle authentication issues (e.g., invalid API key)
  } else if (error instanceof ValidationError) {
    console.error('Validation failed:', error.message);
    // Access validation details
    error.details.forEach(detail => {
      console.error(`- ${detail.field}: ${detail.message}`);
    });
  } else if (error instanceof RateLimitError) {
    console.error('Rate limit exceeded:', error.message);
    console.log(`Try again after ${error.retryAfter} seconds`);
  } else if (error instanceof ResourceNotFoundError) {
    console.error('Resource not found:', error.message);
  } else if (error instanceof ServiceError) {
    console.error('Service error:', error.message);
    console.error('Error code:', error.code);
  } else if (error instanceof NetworkError) {
    console.error('Network error:', error.message);
  } else if (error instanceof AxicovError) {
    // Base error type for any Axicov-specific error
    console.error('Axicov error:', error.message);
    console.error('Request ID:', error.requestId);
  } else {
    // Unknown error
    console.error('Unexpected error:', error);
  }
}

Webhooks

The Axicov SDK includes utilities for verifying and parsing webhooks:

Express Implementation

import express from 'express';
import { verifyWebhookSignature } from '@axicov/sdk';

const app = express();

app.post('/webhooks/axicov', express.raw({ type: 'application/json' }), (req, res) => {
  const signature = req.headers['x-axicov-signature'] as string;
  
  try {
    // Verify webhook signature
    const event = verifyWebhookSignature({
      payload: req.body,
      signature,
      secret: process.env.AXICOV_WEBHOOK_SECRET
    });
    
    // Handle different event types
    switch (event.type) {
      case 'job.completed':
        // Handle completed job
        const jobId = event.data.jobId;
        const jobType = event.data.jobType;
        console.log(`Job completed: ${jobId} (${jobType})`);
        
        // Fetch job results
        handleCompletedJob(jobId, jobType);
        break;
      
      case 'training.completed':
        // Handle completed model training
        const trainingId = event.data.trainingId;
        const modelId = event.data.modelId;
        console.log(`Training completed: ${trainingId} for model ${modelId}`);
        
        // Update model status in your application
        updateModelStatus(modelId, 'ready');
        break;
      
      case 'model.deployed':
        // Handle model deployment
        console.log(`Model deployed: ${event.data.modelId} (version ${event.data.version})`);
        break;
        
      default:
        console.log(`Received event: ${event.type}`);
    }
    
    res.status(200).send('Webhook received');
  } catch (error) {
    console.error('Invalid webhook signature:', error);
    res.status(400).send('Invalid signature');
  }
});

async function handleCompletedJob(jobId, jobType) {
  // Implement job result handling based on job type
}

function updateModelStatus(modelId, status) {
  // Update model status in your application
}

AWS Lambda Implementation

import { verifyWebhookSignature } from '@axicov/sdk';
import { APIGatewayProxyEvent, APIGatewayProxyResult } from 'aws-lambda';

export async function handler(event: APIGatewayProxyEvent): Promise<APIGatewayProxyResult> {
  try {
    // Extract signature from headers
    const signature = event.headers['x-axicov-signature'];
    
    if (!signature) {
      return {
        statusCode: 400,
        body: JSON.stringify({ message: 'Missing signature header' })
      };
    }
    
    // Verify webhook signature
    const webhookEvent = verifyWebhookSignature({
      payload: event.body as string,
      signature,
      secret: process.env.AXICOV_WEBHOOK_SECRET as string
    });
    
    // Process the webhook event
    console.log(`Received webhook event: ${webhookEvent.type}`);
    
    // Handle the event based on its type
    switch (webhookEvent.type) {
      case 'job.completed':
        // Process job completion
        await processCompletedJob(webhookEvent.data);
        break;
      
      // Handle other event types...
    }
    
    return {
      statusCode: 200,
      body: JSON.stringify({ received: true })
    };
  } catch (error) {
    console.error('Webhook processing error:', error);
    
    return {
      statusCode: 400,
      body: JSON.stringify({ message: 'Invalid webhook' })
    };
  }
}

async function processCompletedJob(data: any) {
  // Implement job processing logic
}

Advanced Features

File Handling

The SDK supports various ways to provide image and document data:

// URL reference (simplest approach)
await axicov.vision.detectObjects({
  image: { url: 'https://example.com/image.jpg' }
});

// Buffer with MIME type
import fs from 'fs';
const imageBuffer = await fs.promises.readFile('./local-image.jpg');

await axicov.vision.detectObjects({
  image: { 
    buffer: imageBuffer,
    mimeType: 'image/jpeg'
  }
});

// Base64 encoding
const base64Image = imageBuffer.toString('base64');

await axicov.vision.detectObjects({
  image: { 
    base64: base64Image,
    mimeType: 'image/jpeg'
  }
});

// File stream (for Node.js environments)
const fileStream = fs.createReadStream('./large-image.jpg');

await axicov.vision.detectObjects({
  image: { 
    stream: fileStream,
    mimeType: 'image/jpeg'
  }
});

Handling Large Files

For processing large files, the SDK supports direct upload to secure storage:

// Get a pre-signed upload URL
const uploadUrl = await axicov.getSecureUploadUrl({
  fileType: 'image/jpeg',
  operation: 'vision.detectObjects',
  expiresIn: 3600 // URL expiration in seconds
});

console.log(`Upload URL: ${uploadUrl.url}`);
console.log(`File ID: ${uploadUrl.fileId}`);

// Upload the file to the pre-signed URL (using your preferred HTTP client)
// ...upload file to uploadUrl.url...

// Use the file ID in your API call
const result = await axicov.vision.detectObjects({
  image: { fileId: uploadUrl.fileId },
  confidence: 0.7
});

Request Optimization

Use selective fields to optimize request performance:

// Only include necessary fields in the response
const result = await axicov.vision.analyzeFaces({
  image: { url: 'https://example.com/faces.jpg' },
  attributes: ['age', 'gender'], // Only request needed attributes
  returnFaceAttributes: true,
  returnFaceBoundingBoxes: true,
  returnFaceLandmarks: false, // Skip detailed landmarks to reduce response size
  maxFaces: 10
});

Caching Strategies

Implement caching to improve performance and reduce costs:

import NodeCache from 'node-cache';

// Create a simple cache
const cache = new NodeCache({ stdTTL: 3600 }); // 1 hour TTL

async function getObjectDetection(imageUrl) {
  // Create a cache key based on the request parameters
  const cacheKey = `object-detection:${imageUrl}:0.7`;
  
  // Check if result exists in cache
  const cachedResult = cache.get(cacheKey);
  if (cachedResult) {
    console.log('Using cached result');
    return cachedResult;
  }
  
  // Perform the API call if not in cache
  const result = await axicov.vision.detectObjects({
    image: { url: imageUrl },
    confidence: 0.7
  });
  
  // Store result in cache
  cache.set(cacheKey, result);
  
  return result;
}

Examples

Image Processing Pipeline

Create a complete image processing pipeline:

async function processProductImage(imageUrl) {
  // Detect objects in the image
  const objectResult = await axicov.vision.detectObjects({
    image: { url: imageUrl },
    confidence: 0.7
  });
  
  // Extract text from the image
  const textResult = await axicov.vision.recognizeText({
    image: { url: imageUrl },
    type: 'all'
  });
  
  // Check for inappropriate content
  const moderationResult = await axicov.vision.moderateContent({
    image: { url: imageUrl },
    categories: ['explicit', 'violence']
  });
  
  // Combine the results
  return {
    objects: objectResult.objects,
    text: textResult.text,
    isSafe: !moderationResult.flagged,
    moderationResults: moderationResult
  };
}

Document Processing Workflow

Implement a document processing workflow:

async function processInvoice(fileUrl) {
  // Parse the invoice
  const invoiceData = await axicov.document.parse({
    document: { url: fileUrl },
    type: 'invoice',
    extractTables: true
  });
  
  // Extract key information
  const invoiceNumber = invoiceData.fields['invoice_number']?.text;
  const invoiceDate = invoiceData.fields['invoice_date']?.text;
  const totalAmount = invoiceData.fields['total_amount']?.text;
  
  // Get line items from extracted tables
  const lineItems = [];
  
  if (invoiceData.tables && invoiceData.tables.length > 0) {
    const table = invoiceData.tables[0];
    const headers = table[0];
    
    // Find column indexes
    const descriptionIndex = headers.findIndex(h => /description/i.test(h));
    const quantityIndex = headers.findIndex(h => /quantity|qty/i.test(h));
    const priceIndex = headers.findIndex(h => /price|rate/i.test(h));
    const amountIndex = headers.findIndex(h => /amount|total/i.test(h));
    
    // Process data rows
    for (let i = 1; i < table.length; i++) {
      const row = table[i];
      
      lineItems.push({
        description: descriptionIndex >= 0 ? row[descriptionIndex] : '',
        quantity: quantityIndex >= 0 ? parseFloat(row[quantityIndex]) : 0,
        unitPrice: priceIndex >= 0 ? parseFloat(row[priceIndex]) : 0,
        amount: amountIndex >= 0 ? parseFloat(row[amountIndex]) : 0
      });
    }
  }
  
  return {
    invoiceNumber,
    invoiceDate,
    totalAmount,
    lineItems,
    vendor: invoiceData.fields['vendor_name']?.text,
    confidence: invoiceData.confidence
  };
}

Custom Model Training Pipeline

Implement a complete model training pipeline with monitoring:

async function trainProductDetectionModel(projectName, trainingDataUrl) {
  // 1. Create a new project
  const project = await axicov.custom.createProject({
    name: projectName,
    type: 'object-detection'
  });
  
  console.log(`Created project: ${project.id}`);
  
  // 2. Create a dataset
  const dataset = await axicov.custom.createDataset({
    projectId: project.id,
    name: 'Initial Dataset'
  });
  
  console.log(`Created dataset: ${dataset.id}`);
  
  // 3. Download and prepare training data
  console.log('Downloading training data...');
  const response = await fetch(trainingDataUrl);
  const trainingData = await response.arrayBuffer();
  
  // 4. Upload training data
  console.log('Uploading training data to Axicov

Last updated