ModelFusion

ModelFusion

ModelFusion는 JavaScript와 TypeScript 애플리케이션에 AI 모델을 통합하기 위한 추상화 계층이에요. 텍스트 생성, 텍스트 스트리밍, 객체 생성, 도구 사용 같은 공통 작업의 API를 하나로 통일해 줘요. 관측성(observability) 훅, 로깅, 자동 재시도 같은 프로덕션 환경을 위한 기능도 기본으로 제공해서 AI 애플리케이션, 챗봇, 에이전트를 쉽게 만들 수 있어요.

출처: 문서

본문

ModelFusion은 커뮤니티 기반의 비상업적 오픈소스 프로젝트로, 공급업체에 종속되지 않아요. 지원되는 어떤 프로바이더와도 함께 사용할 수 있어요. 주요 특징은 이렇게 정리할 수 있어요.

  • 벤더 중립 (Vendor-neutral): 비상업적 오픈소스 프로젝트로 커뮤니티가 주도해요. 지원되는 어떤 프로바이더와도 함께 사용할 수 있어요.
  • 멀티모달 (Multi-modal): 텍스트 생성, 이미지 생성, 비전, 텍스트-음성(TTS), 음성-텍스트(STT), 임베딩 모델 등 다양한 모델을 지원해요.
  • 타입 추론과 검증 (Type inference and validation): 가능한 곳에서 TypeScript 타입을 추론하고 모델 응답을 검증해요.
  • 관측성과 로깅 (Observability and logging): 옵저버 프레임워크와 로깅 지원을 제공해요.
  • 탄력성과 견고성 (Resilience and robustness): 자동 재시도, 스로틀링, 오류 처리 메커니즘으로 안정적인 동작을 보장해요.
  • 프로덕션 지향 (Built for production): 완전한 tree-shakeable, 서버리스 환경 지원, 최소한의 의존성만 사용해요.

설치

npm으로 간단히 설치할 수 있어요.

npm install modelfusion

또는 스타터 템플릿을 사용할 수 있어요. 예를 들어 ModelFusion 터미널 앱 스타터, Next.js·Vercel AI SDK·Llama.cpp·ModelFusion 스타터, Next.js·Vercel AI SDK·Ollama·ModelFusion 스타터가 있어요.

API 키는 환경 변수(예: OPENAI_API_KEY)로 제공하거나 모델 생성자의 옵션으로 전달할 수 있어요.

텍스트 생성 (Generate Text)

언어 모델과 프롬프트로 텍스트를 생성해요. 모델이 지원하면 텍스트를 스트리밍할 수도 있어요. 모델이 지원한다면 이미지를 이용한 멀티모달 프롬프팅도 가능해요.

import { generateText, openai } from "modelfusion";

const text = await generateText({
  model: openai.CompletionTextGenerator({ model: "gpt-3.5-turbo-instruct" }),
  prompt: "Write a short story about a robot learning to love:\n\n",
});

텍스트 스트리밍 (streamText)

streamText로 텍스트를 스트리밍 방식으로 생성하면 실시간으로 토큰을 받아올 수 있어요.

import { streamText, openai } from "modelfusion";

const textStream = await streamText({
  model: openai.CompletionTextGenerator({ model: "gpt-3.5-turbo-instruct" }),
  prompt: "Write a short story about a robot learning to love:\n\n",
});

for await (const textPart of textStream) {
  process.stdout.write(textPart);
}

멀티모달 프롬프트 스트리밍

GPT-4 Vision 같은 멀티모달 비전 모델은 프롬프트의 일부로 이미지를 처리할 수 있어요.

import { streamText, openai } from "modelfusion";
import { readFileSync } from "fs";

const image = readFileSync("./image.png");

const textStream = await streamText({
  model: openai
    .ChatTextGenerator({ model: "gpt-4-vision-preview" })
    .withInstructionPrompt(),

  prompt: {
    instruction: [
      { type: "text", text: "Describe the image in detail." },
      { type: "image", image, mimeType: "image/png" },
    ],
  },
});

for await (const textPart of textStream) {
  process.stdout.write(textPart);
}

구조화된 객체 생성 (Generate Object)

언어 모델과 스키마로 타입이 있는 객체를 생성해요. generateObject는 스키마에 맞는 객체를 반환해요. 아래 예시는 Ollama와 zod 스키마로 감성 분석 결과 객체를 만드는 코드예요.

import {
  ollama,
  zodSchema,
  generateObject,
  jsonObjectPrompt,
} from "modelfusion";

const sentiment = await generateObject({
  model: ollama
    .ChatTextGenerator({
      model: "openhermes2.5-mistral",
      maxGenerationTokens: 1024,
      temperature: 0,
    })
    .asObjectGenerationModel(jsonObjectPrompt.instruction()),

  schema: zodSchema(
    z.object({
      sentiment: z
        .enum(["positive", "neutral", "negative"])
        .describe("Sentiment."),
    })
  ),

  prompt: {
    system:
      "You are a sentiment evaluator. " +
      "Analyze the sentiment of the following product review:",
    instruction:
      "After I opened the package, I was met by a very unpleasant smell " +
      "that did not disappear even after washing. Never again!",
  },
});

객체를 스트리밍하려면 streamObject를 사용해요. 최종 부분 이전의 부분 객체는 타입이 없는 JSON이에요.

이미지 생성 (Generate Image)

프롬프트로 이미지를 생성할 수 있어요.

import { generateImage, openai } from "modelfusion";

const image = await generateImage({
  model: openai.ImageGenerator({ model: "dall-e-3", size: "1024x1024" }),
  prompt:
    "the wicked witch of the west in the style of early 19th century painting",
});

음성 생성 (Generate Speech)

텍스트에서 음성(오디오)을 합성해요. TTS(text-to-speech)라고도 불러요. generateSpeech는 텍스트에서 음성을 합성하고, streamSpeech는 텍스트 또는 텍스트 스트림에서 음성 청크를 스트리밍으로 생성해요.

음성-텍스트 변환 (Generate Transcription)

음성(오디오) 데이터를 텍스트로 변환해요. STT(speech-to-text)라고도 불러요.

import { generateTranscription, openai } from "modelfusion";
import fs from "node:fs";

const transcription = await generateTranscription({
  model: openai.Transcriber({ model: "whisper-1" }),
  mimeType: "audio/mp3",
  audioData: await fs.promises.readFile("data/test.mp3"),
});

임베딩과 분류 (Embed & Classify)

텍스트 등 값을 임베딩(벡터)으로 만드는 embed/embedMany와, 값을 카테고리로 분류하는 classify도 제공해요.

import { embed, embedMany, openai } from "modelfusion";

// embed single value:
const embedding = await embed({
  model: openai.TextEmbedder({ model: "text-embedding-ada-002" }),
  value: "At first, Nox didn't know what to do with the pup.",
});

// embed many values:
const embeddings = await embedMany({
  model: openai.TextEmbedder({ model: "text-embedding-ada-002" }),
  values: [
    "At first, Nox didn't know what to do with the pup.",
    "He keenly observed and absorbed everything around him, from the birds in the sky to the trees in the forest.",
  ],
});

도구 (Tools)와 에이전트

Tools는 AI 모델이 실행할 수 있는 함수(및 연관 메타데이터)예요. 챗봇과 에이전트를 만드는 데 유용해요. ModelFusion은 Math.js, MediaWiki Search, SerpAPI, Google Custom Search 같은 도구를 기본으로 제공하고, 직접 커스텀 도구도 만들 수 있어요.

runTool은 단일 도구 호출을, runTools는 여러 도구 호출과 텍스트를 함께 생성하고 실행해요. runTools로 사용자 메시지에 응답하고 도구를 실행하는 에이전트 루프(agent loop)도 구현할 수 있어요.

const { text, toolResults } = await runTools({
  model: openai.ChatTextGenerator({ model: "gpt-3.5-turbo" }),
  tools: [calculator /* ... */],
  prompt: [openai.ChatMessage.user("What's fourteen times twelve?")],
});

벡터 인덱스 (Vector Indices)

텍스트를 임베딩해 벡터 인덱스에 저장하고, 유사한 텍스트를 검색해올 수 있어요. Memory, SQLite VSS, Pinecone 벡터 스토어를 지원해요.

const texts = [
  "A rainbow is an optical phenomenon that can occur under certain meteorological conditions.",
  "It is caused by refraction, internal reflection and dispersion of light in water droplets resulting in a continuous spectrum of light appearing in the sky.",
  // ...
];

const vectorIndex = new MemoryVectorIndex<string>();
const embeddingModel = openai.TextEmbedder({
  model: "text-embedding-ada-002",
});

// update an index - usually done as part of an ingestion process:
await upsertIntoVectorIndex({
  vectorIndex,
  embeddingModel,
  objects: texts,
  getValueToEmbed: (text) => text,
});

// retrieve text chunks from the vector index - usually done at query time:
const retrievedTexts = await retrieve(
  new VectorIndexRetriever({
    vectorIndex,
    embeddingModel,
    maxResults: 3,
    similarityThreshold: 0.8,
  }),
  "rainbow and water droplets"
);

로깅과 관측성 (Logging & Observability)

ModelFusion은 옵저버 프레임워크와 로깅 지원을 제공해요. 실행과 호출 계층을 쉽게 추적할 수 있고, 자신만의 옵저버를 추가할 수도 있어요. 함수 호출에 logging 옵션을 넣으면 로깅을 켤 수 있어요.

import { generateText, openai } from "modelfusion";

const text = await generateText({
  model: openai.CompletionTextGenerator({ model: "gpt-3.5-turbo-instruct" }),
  prompt: "Write a short story about a robot learning to love:\n\n",
  logging: "detailed-object",
});

한 가지 참고할 점은, ModelFusion이 Vercel에 합류하면서 Vercel AI SDK에 통합되고 있다는 점이에요. 텍스트 생성, 구조화된 객체 생성, 도구 호출을 시작으로 ModelFusion의 좋은 기능들을 Vercel AI SDK로 옮기고 있으니, 최신 개발 내용은 Vercel AI SDK를 확인하면 좋아요.

더 알아보기 (Learn more)