> ## Documentation Index
> Fetch the complete documentation index at: https://apidocs.mor.org/llms.txt
> Use this file to discover all available pages before exploring further.

# Vercel AI SDK v5 Integration

> Complete guide to integrating the Morpheus Inference API with Vercel's AI SDK v5 for streaming text generation and tool calling

# Integrate Morpheus Inference API with Vercel AI SDK

Learn how to integrate the Morpheus Inference API with Vercel's AI SDK v5 to build AI-powered applications with decentralized AI inference. This guide covers streaming responses, tool calling, and handling Morpheus-specific implementation details.

## Overview

The Morpheus Inference API provides **AI inference** through a decentralized compute marketplace. By integrating with Vercel's AI SDK, you get access to powerful models like Qwen, Llama, and more while maintaining a familiar OpenAI-compatible API structure.

## Prerequisites

Before you begin, ensure you have:

* **Node.js 18+** installed on your system
* A **Morpheus API key** from [app.mor.org](https://app.mor.org)
* Basic knowledge of **Next.js** and **React**
* Familiarity with **TypeScript**

<Steps>
  <Step title="Create a Morpheus API Key">
    Visit [app.mor.org](https://app.mor.org) and sign in to create your API key.

    1. Navigate to the API Keys section
    2. Click "Create API Key" and provide a name
    3. Copy your API key immediately (it won't be shown again)

    <Warning>
      Store your API key securely. Never commit it to version control or expose it in client-side code.
    </Warning>
  </Step>

  <Step title="Install Required Dependencies">
    Install the Vercel AI SDK and OpenAI-compatible provider:

    ```bash theme={null}
    npm install ai @ai-sdk/openai-compatible zod
    ```

    <Check>
      Verify installation by running `npm list ai` to see the installed version.
    </Check>
  </Step>

  <Step title="Configure Environment Variables">
    Create a `.env.local` file in your project root:

    ```bash .env.local theme={null}
    MORPHEUS_API_KEY=your_api_key_here
    ```

    For production, you can optionally use type-safe environment validation with `@t3-oss/env-nextjs`:

    ```typescript src/lib/env.ts theme={null}
    import { createEnv } from "@t3-oss/env-nextjs";
    import { z } from "zod";

    export const env = createEnv({
      server: {
        MORPHEUS_API_KEY: z.string(),
      },
      runtimeEnv: {
        MORPHEUS_API_KEY: process.env.MORPHEUS_API_KEY,
      },
      emptyStringAsUndefined: true,
    });
    ```

    <Warning>
      Never commit your API key to version control. Add `.env.local` to your `.gitignore` file.
    </Warning>
  </Step>
</Steps>

## Basic Integration

### Setting Up the Morpheus Provider

The Morpheus Inference API is OpenAI-compatible, allowing you to use the `@ai-sdk/openai-compatible` provider:

```typescript lib/morpheus.ts theme={null}
import { createOpenAICompatible } from '@ai-sdk/openai-compatible';

export const morpheus = createOpenAICompatible({
  name: 'morpheus',
  apiKey: process.env.MORPHEUS_API_KEY!,
  baseURL: 'https://api.mor.org/api/v1',
});
```

<Tip>
  The `createOpenAICompatible` provider allows any OpenAI-compatible API to work seamlessly with the AI SDK, including Morpheus.
</Tip>

### Available Models

Query the available models using the Morpheus API:

```bash theme={null}
curl -X GET 'https://api.mor.org/api/v1/models' \
  -H 'Authorization: Bearer YOUR_API_KEY' \
  -H 'Content-Type: application/json'
```

<Accordion title="Common Morpheus Models">
  Popular models available through Morpheus:

  * **llama-3.3-70b:web** - Meta's Llama 3.3 with web search tool calling
  * **llama-3.3-70b** - Meta's Llama 3.3 base model
  * **glm-5.2:web** - GLM 5.2 with web search and reasoning
  * **glm-5.2** - GLM 5.2 with reasoning and function calling
  * **glm-5.1:web** - GLM 5.1 with web search and reasoning
  * **glm-5.1** - GLM 5.1 with reasoning and function calling
  * **kimi-k2.7-code:web** - Kimi K2.7 Code with web search
  * **kimi-k2.7-code** - Kimi K2.7 Code with reasoning and function calling
  * **kimi-k2.6:web** - Kimi K2.6 with web search and vision
  * **kimi-k2.6** - Kimi K2.6 with reasoning, vision, and function calling
  * **MiniMax-M2.7:web** - MiniMax M2.7 with web search
  * **MiniMax-M2.7** - MiniMax M2.7 with reasoning and function calling
  * **deepseek-v4-pro:web** - DeepSeek V4 Pro with web search (1M context)
  * **deepseek-v4-pro** - DeepSeek V4 Pro with frontier reasoning and function calling (1M context)
  * **deepseek-v4-flash:web** - DeepSeek V4 Flash with web search (1M context)
  * **deepseek-v4-flash** - DeepSeek V4 Flash for fast frontier-tier reasoning (1M context)
  * **qwen3-235b:web** - Qwen 3 with web search tool calling
  * **qwen3-235b** - Qwen 3 base model

  <Info>
    Model availability may vary based on provider availability in the Morpheus marketplace. The API automatically routes to the highest-rated provider for your selected model. The `:web` suffix indicates models optimized for web content and browsing tasks.
  </Info>
</Accordion>

## Text Generation

### Basic Text Generation

Use the `generateText` function for simple, non-streaming text generation:

```typescript server-action.ts theme={null}
'use server';

import { generateText } from 'ai';
import { morpheus } from '@/lib/morpheus';

export async function generateRecipe(prompt: string) {
  const { text } = await generateText({
    model: morpheus('llama-3.3-70b'),
    prompt,
    maxTokens: 1024,
  });

  return text;
}
```

### Streaming Text Generation

For interactive applications, use `streamText` to stream responses in real-time:

```typescript app/api/chat/route.ts theme={null}
import { streamText } from 'ai';
import { morpheus } from '@/lib/morpheus';

export async function POST(req: Request) {
  const { prompt } = await req.json();

  const result = streamText({
    model: morpheus('llama-3.3-70b:web'),
    prompt,
    temperature: 0.7,
  });

  return result.toDataStreamResponse();
}
```

<Tip>
  Use `toDataStreamResponse()` for easy integration with AI SDK UI components. For more control, use `toTextStreamResponse()` or iterate over `result.textStream` directly.
</Tip>

## Complete API Route Implementation

Here's a complete example of a chat API route using Morpheus:

```typescript app/api/chat/route.ts theme={null}
import { streamText, convertToModelMessages } from 'ai';
import { morpheus } from '@/lib/morpheus';

export async function POST(req: Request) {
  const { messages, model = 'llama-3.3-70b:web' } = await req.json();

  const result = streamText({
    model: morpheus(model),
    messages: convertToModelMessages(messages),
    temperature: 0.7,
    maxTokens: 2048,
  });

  return result.toDataStreamResponse();
}
```

<Info>
  The `convertToModelMessages` function transforms AI SDK UI messages into the format expected by language models. It handles user messages, assistant messages, and system prompts automatically.
</Info>

## Tool Calling

Enable your AI models to execute functions and interact with external systems through tool calling.

### Defining Tools

Define tools using Zod schemas for type-safe parameter validation:

```typescript lib/tools.ts theme={null}
import { tool } from 'ai';
import { z } from 'zod';

export const tools = {
  get_random_number: tool({
    description: 'Generate a random number within a specified range',
    parameters: z.object({
      min: z.number().describe('Minimum value (inclusive)'),
      max: z.number().describe('Maximum value (inclusive)'),
    }),
    execute: async ({ min, max }) => {
      const random = Math.floor(Math.random() * (max - min + 1)) + min;
      return { number: random };
    },
  }),

  get_weather: tool({
    description: 'Get current weather for a location',
    parameters: z.object({
      city: z.string().describe('City name'),
      country: z.string().optional().describe('Country code (e.g., US)'),
    }),
    execute: async ({ city, country }) => {
      // Call weather API
      const response = await fetch(
        `https://api.weatherapi.com/v1/current.json?key=${process.env.WEATHER_API_KEY}&q=${city},${country || ''}`
      );
      const data = await response.json();
      
      return {
        temperature: data.current.temp_c,
        condition: data.current.condition.text,
      };
    },
  }),
};
```

### Using Tools with Streaming

Define tools using Zod schemas and integrate them with your streaming endpoint:

```typescript app/api/chat/route.ts theme={null}
import { streamText, convertToModelMessages, tool } from 'ai';
import { morpheus } from '@/lib/morpheus';
import { z } from 'zod';

export async function POST(req: Request) {
  const { messages, model = 'llama-3.3-70b:web' } = await req.json();

  const result = streamText({
    model: morpheus(model),
    messages: convertToModelMessages(messages),
    tools: {
      get_weather: tool({
        description: 'Get current weather for a location',
        parameters: z.object({
          city: z.string().describe('City name'),
        }),
        execute: async ({ city }) => {
          // Your weather API call here
          return {
            temperature: 72,
            condition: 'sunny',
            city,
          };
        },
      }),
      calculate: tool({
        description: 'Perform a mathematical calculation',
        parameters: z.object({
          expression: z.string().describe('Math expression to evaluate'),
        }),
        execute: async ({ expression }) => {
          // Safely evaluate the expression
          const result = eval(expression);
          return { result };
        },
      }),
    },
    maxSteps: 5, // Limit tool calling iterations
  });

  return result.toDataStreamResponse();
}
```

<Tip>
  Use `maxSteps` to prevent infinite tool calling loops. The AI SDK will automatically handle multi-step tool execution.
</Tip>

### Tool Calling Best Practices

<CardGroup cols={2}>
  <Card title="Clear descriptions" icon="message">
    Provide detailed descriptions for tools and parameters to help the model understand when and how to use them.
  </Card>

  <Card title="Validate inputs" icon="shield-check">
    Use Zod schemas to enforce parameter types and constraints, preventing invalid tool executions.
  </Card>

  <Card title="Handle errors gracefully" icon="triangle-exclamation">
    Wrap tool execution logic in try-catch blocks and return meaningful error messages to the model.
  </Card>

  <Card title="Limit steps" icon="gauge">
    Use `stopWhen: stepCountIs(n)` to prevent infinite tool calling loops and control costs.
  </Card>
</CardGroup>

## Client-Side Implementation

Build an interactive chat interface using the AI SDK's `useChat` hook:

```typescript app/page.tsx theme={null}
'use client';

import { useChat } from 'ai/react';
import { useState } from 'react';

const models = [
  { name: 'GLM 5.2 (Web)', value: 'glm-5.2:web' },
  { name: 'GLM 5.2', value: 'glm-5.2' },
  { name: 'GLM 5.1 (Web)', value: 'glm-5.1:web' },
  { name: 'GLM 5.1', value: 'glm-5.1' },
  { name: 'Kimi K2.7 Code (Web)', value: 'kimi-k2.7-code:web' },
  { name: 'Kimi K2.7 Code', value: 'kimi-k2.7-code' },
  { name: 'Kimi K2.6 (Web)', value: 'kimi-k2.6:web' },
  { name: 'Kimi K2.6', value: 'kimi-k2.6' },
  { name: 'MiniMax M2.7 (Web)', value: 'MiniMax-M2.7:web' },
  { name: 'MiniMax M2.7', value: 'MiniMax-M2.7' },
  { name: 'DeepSeek V4 Pro (Web)', value: 'deepseek-v4-pro:web' },
  { name: 'DeepSeek V4 Pro', value: 'deepseek-v4-pro' },
  { name: 'DeepSeek V4 Flash (Web)', value: 'deepseek-v4-flash:web' },
  { name: 'DeepSeek V4 Flash', value: 'deepseek-v4-flash' },
  { name: 'Llama 3.3 70B (Web)', value: 'llama-3.3-70b:web' },
  { name: 'Llama 3.3 70B', value: 'llama-3.3-70b' },
  { name: 'Qwen 3 235B (Web)', value: 'qwen3-235b:web' },
  { name: 'Qwen 3 235B', value: 'qwen3-235b' },
];

export default function ChatPage() {
  const [selectedModel, setSelectedModel] = useState(models[0].value);
  
  const { messages, input, handleInputChange, handleSubmit, isLoading } = useChat({
    api: '/api/chat',
    body: { model: selectedModel },
  });

  return (
    <div className="flex flex-col h-screen max-w-3xl mx-auto p-6">
      {/* Model selector */}
      <div className="mb-6">
        <label className="block text-sm font-medium mb-2">Select Model</label>
        <select
          value={selectedModel}
          onChange={(e) => setSelectedModel(e.target.value)}
          className="w-full p-2 border rounded-lg"
          disabled={isLoading}
        >
          {models.map((model) => (
            <option key={model.value} value={model.value}>
              {model.name}
            </option>
          ))}
        </select>
      </div>

      {/* Messages */}
      <div className="flex-1 overflow-y-auto space-y-4 mb-4">
        {messages.map((message) => (
          <div
            key={message.id}
            className={`p-4 rounded-lg ${
              message.role === 'user' 
                ? 'bg-blue-100 ml-auto max-w-[80%]' 
                : 'bg-gray-100 max-w-[80%]'
            }`}
          >
            <div className="font-semibold mb-2 text-sm">
              {message.role === 'user' ? 'You' : 'Assistant'}
            </div>
            <div className="whitespace-pre-wrap">{message.content}</div>
            
            {/* Tool invocations */}
            {message.toolInvocations?.map((tool) => (
              <div key={tool.toolCallId} className="mt-3 p-3 bg-white rounded border">
                <div className="text-sm font-mono text-gray-700">
                  🔧 {tool.toolName}
                </div>
                <div className="text-xs text-gray-500 mt-1">
                  {JSON.stringify(tool.args, null, 2)}
                </div>
                {tool.state === 'result' && (
                  <div className="text-sm text-green-600 mt-2">
                    ✓ {JSON.stringify(tool.result)}
                  </div>
                )}
              </div>
            ))}
          </div>
        ))}
        
        {isLoading && (
          <div className="p-4 bg-gray-100 rounded-lg animate-pulse">
            Thinking...
          </div>
        )}
      </div>

      {/* Input form */}
      <form onSubmit={handleSubmit} className="flex gap-2">
        <input
          value={input}
          onChange={handleInputChange}
          placeholder="Ask anything..."
          className="flex-1 p-3 border rounded-lg focus:outline-none focus:ring-2 focus:ring-blue-500"
          disabled={isLoading}
        />
        <button
          type="submit"
          disabled={isLoading || !input.trim()}
          className="px-6 py-3 bg-blue-500 text-white rounded-lg hover:bg-blue-600 disabled:bg-gray-300 disabled:cursor-not-allowed transition-colors"
        >
          Send
        </button>
      </form>
    </div>
  );
}
```

<Info>
  The `useChat` hook automatically handles message state, streaming updates, and tool invocations. It provides a simple interface for building chat applications with minimal boilerplate.
</Info>

## Model Selection

Allow users to switch between different Morpheus models:

```typescript app/page.tsx theme={null}
const models = [
  { name: 'GLM 5.2 (Web)', value: 'glm-5.2:web' },
  { name: 'GLM 5.2', value: 'glm-5.2' },
  { name: 'GLM 5.1 (Web)', value: 'glm-5.1:web' },
  { name: 'GLM 5.1', value: 'glm-5.1' },
  { name: 'Kimi K2.7 Code (Web)', value: 'kimi-k2.7-code:web' },
  { name: 'Kimi K2.7 Code', value: 'kimi-k2.7-code' },
  { name: 'Kimi K2.6 (Web)', value: 'kimi-k2.6:web' },
  { name: 'Kimi K2.6', value: 'kimi-k2.6' },
  { name: 'MiniMax M2.7 (Web)', value: 'MiniMax-M2.7:web' },
  { name: 'MiniMax M2.7', value: 'MiniMax-M2.7' },
  { name: 'DeepSeek V4 Pro (Web)', value: 'deepseek-v4-pro:web' },
  { name: 'DeepSeek V4 Pro', value: 'deepseek-v4-pro' },
  { name: 'DeepSeek V4 Flash (Web)', value: 'deepseek-v4-flash:web' },
  { name: 'DeepSeek V4 Flash', value: 'deepseek-v4-flash' },
  { name: 'Llama 3.3 70B (Web)', value: 'llama-3.3-70b:web' },
  { name: 'Llama 3.3 70B', value: 'llama-3.3-70b' },
  { name: 'Qwen 3 235B (Web)', value: 'qwen3-235b:web' },
  { name: 'Qwen 3 235B', value: 'qwen3-235b' },
];

export default function ChatPage() {
  const [selectedModel, setSelectedModel] = useState(models[0].value);
  
  const { messages, input, handleInputChange, handleSubmit } = useChat({
    api: '/api/chat',
    body: { model: selectedModel }, // Pass selected model to API
  });

  return (
    <select
      value={selectedModel}
      onChange={(e) => setSelectedModel(e.target.value)}
    >
      {models.map((model) => (
        <option key={model.value} value={model.value}>
          {model.name}
        </option>
      ))}
    </select>
  );
}
```

Your API route receives the model parameter and passes it to Morpheus:

```typescript app/api/chat/route.ts theme={null}
export async function POST(req: Request) {
  const { messages, model = 'llama-3.3-70b:web' } = await req.json();

  const result = streamText({
    model: morpheus(model), // Use the selected model
    messages: convertToModelMessages(messages),
  });

  return result.toDataStreamResponse();
}
```

<Tip>
  The `:web` suffix indicates models optimized for web browsing and content generation. These models typically perform better for tasks involving current events or web-based information.
</Tip>

## Troubleshooting

<AccordionGroup>
  <Accordion title="Tool calls fail with 'Expected function.name to be a string'">
    **Cause**: Morpheus may send tool call metadata and arguments in separate chunks during streaming.

    **Solution**: This issue has been resolved in recent versions of the Morpheus API. If you still encounter it, implement a custom stream transformer:

    ```typescript theme={null}
    const customFetch = async (url: RequestInfo, init?: RequestInit) => {
      const response = await fetch(url, init);
      
      if (!response.body) return response;

      let toolCallBuffer: Record<string, { arguments: string }> = {};

      const transformStream = new TransformStream({
        transform(chunk, controller) {
          const text = new TextDecoder().decode(chunk);
          const lines = text.split('\n');
          let modifiedChunk = '';

          for (const line of lines) {
            if (!line.startsWith('data: ') || line === 'data: [DONE]') {
              modifiedChunk += line + '\n';
              continue;
            }

            try {
              const data = JSON.parse(line.substring(6));
              
              if (data.choices?.[0]?.delta?.tool_calls) {
                for (const toolCall of data.choices[0].delta.tool_calls) {
                  const key = `${toolCall.index || 0}`;
                  
                  if (!toolCallBuffer[key]) {
                    toolCallBuffer[key] = { arguments: '' };
                  }

                  // Remove initial empty "{}" but keep metadata
                  if (toolCall.function?.arguments === '{}' && 
                      toolCallBuffer[key].arguments === '') {
                    delete toolCall.function.arguments;
                  } else if (toolCall.function?.arguments) {
                    toolCallBuffer[key].arguments += toolCall.function.arguments;
                  }
                }
              }

              modifiedChunk += `data: ${JSON.stringify(data)}\n`;
            } catch {
              modifiedChunk += line + '\n';
            }
          }

          controller.enqueue(new TextEncoder().encode(modifiedChunk));
        },
      });

      return new Response(response.body.pipeThrough(transformStream), {
        headers: response.headers,
      });
    };

    // Use in createModel:
    const morpheus = createOpenAICompatible({
      name: 'morpheus',
      apiKey: env.MORPHEUS_AI_API_KEY,
      baseURL: 'https://api.mor.org/api/v1',
      fetch: customFetch,
    });
    ```
  </Accordion>

  <Accordion title="Model calls tools for simple questions">
    **Cause**: The system prompt doesn't provide clear guidance on when to use tools vs. direct answers.

    **Solution**: Add explicit instructions in your system prompt:

    ```typescript theme={null}
    const result = streamText({
      model: morpheus('llama-3.3-70b:web'),
      system: `You are a helpful AI assistant. Answer simple questions directly without using tools. Only use tools when you need to:
      - Access external data or APIs
      - Perform complex calculations
      - Execute actions that require tool capabilities`,
      messages,
      tools,
    });
    ```
  </Accordion>

  <Accordion title="Stream stops with 'finishReason: unknown'">
    **Cause**: Morpheus may be sending error JSON mixed into the SSE stream.

    **Solution**: Ensure your transformer filters out non-SSE error messages:

    ```typescript theme={null}
    // Add this check in your transformer:
    if (line.startsWith('{') && line.includes('"error"')) {
      console.log('[MORPHEUS FIX] Filtering error JSON:', line);
      continue; // Skip this line
    }
    ```
  </Accordion>

  <Accordion title="Multi-parameter tool calls fail">
    **Cause**: Some Morpheus models struggle with complex tool calls requiring multiple parameters.

    **Solution**: Try a different model (llama-3.3-70b often performs better) or simplify your tools. Provide explicit examples in tool descriptions:

    ```typescript theme={null}
    description: 'Generate a random number. Example: for "1 to 10", use min=1, max=10'
    ```
  </Accordion>

  <Accordion title="API returns 'invalid response format' errors">
    **Cause**: Morpheus occasionally sends malformed error responses that break SSE parsing.

    **Solution**: The stream transformer should filter these out. Add comprehensive logging to identify problematic chunks:

    ```typescript theme={null}
    if (text.includes('tool_calls') || text.includes('finish_reason') || text.includes('error')) {
      console.log('[MORPHEUS FULL CHUNK]:', text);
    }
    ```
  </Accordion>
</AccordionGroup>

## Advanced Configuration

### Custom Headers and Options

Pass additional configuration to the Morpheus provider:

```typescript lib/morpheus.ts theme={null}
export const morpheus = createOpenAICompatible({
  name: 'morpheus',
  apiKey: process.env.MORPHEUS_API_KEY!,
  baseURL: 'https://api.mor.org/api/v1',
  headers: {
    'X-Custom-Header': 'value',
  },
});
```

### Token Usage Tracking

Track token consumption using the `onFinish` callback:

```typescript theme={null}
const result = streamText({
  model: morpheus('llama-3.3-70b'),
  messages,
  onFinish: async ({ text, usage, finishReason }) => {
    console.log('Generation complete:', {
      textLength: text.length,
      promptTokens: usage.promptTokens,
      completionTokens: usage.completionTokens,
      totalTokens: usage.totalTokens,
      finishReason,
    });
    
    // Log to database or analytics
    await logUsage({
      model: 'llama-3.3-70b',
      tokens: usage.totalTokens,
      timestamp: new Date(),
    });
  },
});
```

### Error Handling

Implement robust error handling for production applications:

```typescript app/api/chat/route.ts theme={null}
export async function POST(req: Request) {
  try {
    const { messages, model = 'llama-3.3-70b:web' } = await req.json();

    const result = streamText({
      model: morpheus(model),
      messages: convertToModelMessages(messages),
      maxRetries: 2, // Retry failed requests
      temperature: 0.7,
    });

    return result.toDataStreamResponse();
  } catch (error) {
    console.error('Chat error:', error);

    // Return user-friendly error
    return new Response(
      JSON.stringify({
        error: 'Failed to generate response. Please try again.',
        details: error instanceof Error ? error.message : 'Unknown error',
      }),
      {
        status: 500,
        headers: { 'Content-Type': 'application/json' },
      }
    );
  }
}
```

<Tip>
  Use `maxRetries` to automatically retry failed requests. This is especially useful for handling temporary network issues or provider timeouts.
</Tip>

## Next Steps

<CardGroup cols={2}>
  <Card title="Explore Models" icon="sparkles" href="https://morpheus.mintlify.app/documentation/how-to/viewing-models">
    Browse all available models in the Morpheus marketplace and their capabilities.
  </Card>

  <Card title="AI SDK Documentation" icon="book" href="https://sdk.vercel.ai/docs">
    Dive deeper into the Vercel AI SDK's features, including agents, embeddings, and more.
  </Card>

  <Card title="Morpheus API Reference" icon="code" href="https://morpheus.mintlify.app/api-reference/introduction">
    Complete API documentation for all Morpheus Gateway endpoints.
  </Card>

  <Card title="Example Projects" icon="github" href="https://github.com/vercel/ai">
    Explore example projects and templates using the AI SDK.
  </Card>
</CardGroup>

## Summary

You've successfully integrated the Morpheus Inference API with Vercel's AI SDK! Key takeaways:

<Check>
  **OpenAI Compatibility**: Morpheus works seamlessly with the AI SDK's OpenAI-compatible provider
</Check>

<Check>
  **Streaming Support**: Real-time streaming responses work out of the box with `streamText`
</Check>

<Check>
  **Tool Calling**: Define tools with Zod schemas for type-safe, multi-step interactions
</Check>

<Check>
  **Model Selection**: Choose between different Morpheus models (Llama, Qwen) based on your needs
</Check>

The combination of Morpheus's decentralized AI inference and the AI SDK's powerful abstractions enables you to build sophisticated AI applications without infrastructure costs or vendor lock-in.
