본문으로 건너뛰기

GenAI 서버

브라우저, UI, 서비스 또는 원격 클라이언트가 HTTP를 통해 하나 이상의 GenAI 모델을 호출해야 하는 경우 GenAIServer를 사용합니다. 서버는 모델 등록, 요청 라우팅, 스트리밍 응답 및 취소를 담당합니다. 모델과 동일한 프로세스에서 실행되는 애플리케이션 로직의 경우 GenAI API를 직접 사용를 사용합니다.

서버 생성 및 시작

서버는 기본적으로 0.0.0.0:9998에 바인딩됩니다. 서버를 시작하기 전에 배포된 각 LLiMa 모델 디렉터리를 안정적인 서비스 이름으로 등록합니다.

#include <neat/genai.h>

int main() {
simaai::neat::genai::GenAIServer server;
server.add_model(
"/media/nvme/llima/models/Qwen3-4B-Instruct-2507-GPTQ-a16w4",
"llm");
server.add_model(
"/media/nvme/llima/models/Qwen3-VL-4B-Instruct-GPTQ-a16w4",
"vlm");
server.serve();
}

C++에서는 차단되는 전경 서버를 위해 serve()를 사용합니다. C++ 또는 Python 애플리케이션이 서버 수명을 관리할 때는 start()stop()을 사용합니다. 기본 호스트 또는 포트가 적절하지 않을 때는 GenAIServerOptions를 구성합니다. stop()을 호출하면 등록된 모든 모델이 제거되므로, 동일한 서버 객체를 다시 시작하기 전에 모델을 다시 추가해야 합니다.

GenAIServer는 인증 또는 TLS 종료 기능을 제공하지 않으며, 모든 출처의 CORS 요청을 허용합니다. 신뢰할 수 있는 인터페이스에만 바인딩하거나 필요한 접근 제어 및 암호화를 제공하는 네트워크 계층 뒤에 배치합니다.

제공되는 모델 확인

생성 요청을 보내기 전에 등록된 모델 이름을 나열합니다.

curl http://<modalix-ip>:9998/v1/models

응답은 OpenAI 모델 목록 형식을 사용합니다.

{
"object": "list",
"data": [
{"id": "llm", "object": "model", "owned_by": "simaai"},
{"id": "vlm", "object": "model", "owned_by": "simaai"}
]
}

각 세대 및 오디오 요청은 해당 model 필드에서 이러한 제공된 이름 중 하나를 사용해야 합니다.

엔드포인트

메서드경로목적
GET/v1/models등록된 제공 모델 이름 목록.
POST/v1/chat/completions텍스트, 이미지, 도구 및 스트리밍을 포함한 OpenAI 호환 채팅.
POST/v1/completionsOpenAI 호환 프롬프트 완성.
POST/v1/audio/transcriptions다중 부분 오디오 입력을 텍스트로 변환.
POST/v1/audio/translations다중 부분 음성을 영어로 번역.
POST/api/chatOllama 호환 채팅; 기본적으로 NDJSON을 스트리밍합니다.
POST/api/generateOllama 호환 프롬프트 생성; 기본적으로 NDJSON을 스트리밍합니다.
POST/stop하나의 모델 또는 모든 모델에 대한 활성 스트림을 취소합니다.
POST/set_lora동적 LoRA 어댑터를 활성화하거나 대체합니다.
POST/unset_lora동적으로 조정된 모델을 기본 가중치로 되돌립니다.

호환성 별칭 /audio/transcriptions/audio/translations도 허용됩니다. 새 클라이언트에서는 /v1/audio/... 경로를 사용하는 것이 좋습니다.

호환성은 GenAIServer에서 구현된 경로 및 응답 형식을 의미하며, 상위 OpenAI 또는 Ollama API의 모든 필드에 대한 지원을 의미하지는 않습니다. 지원되는 요청 필드는 아래에 설명되어 있습니다.

OpenAI 호환 요청

채팅 엔드포인트에 modelmessages를 보냅니다. 서버 전송 이벤트의 경우 streamtrue로 설정합니다. 기본값은 false입니다.

curl http://<modalix-ip>:9998/v1/chat/completions \
-H "Content-Type: application/json" \
-d '{
"model": "llm",
"messages": [
{"role": "user", "content": "Explain an API gateway in one sentence."}
],
"max_tokens": 64,
"stream": false
}'

/v1/chat/completionsmodel, messagesstream 외에도 max_tokens 또는 max_completion_tokens, toolstool_choice를 허용합니다. 도구 정의는 OpenAI의 함수-도구 형식을 사용합니다. tool_choice"auto""none"을 지원합니다. 이를 생략하거나 null로 설정하면 기본 도구 동작이 유지됩니다.

/v1/completions는 문자열 또는 문자열 배열인 prompt와 함께 model, max_tokens 또는 max_completion_tokensstream를 허용하며, 기본값은 false입니다. prompt가 배열인 경우 서버는 해당 문자열 항목을 줄 바꿈 문자로 연결하여 하나의 완료 요청을 실행합니다.

VLM 요청의 경우 OpenAI 채팅 image_url 콘텐츠 부분에는 data:image/jpeg;base64,...와 같은 base64 데이터 URI가 포함되어야 합니다. 이미지 디코딩에는 OpenCV 지원이 있는 Neat 빌드가 필요합니다.

Ollama 호환 요청

메시지 기록에는 /api/chat를, 프롬프트에는 /api/generate를 사용하십시오.

curl http://<modalix-ip>:9998/api/generate \
-H "Content-Type: application/json" \
-d '{
"model": "llm",
"prompt": "Give me three API design tips.",
"options": {"num_predict": 96},
"stream": false
}'

Ollama와 호환되는 모든 엔드포인트는 기본적으로 줄 바꿈 문자로 구분된 JSON을 스트리밍합니다. 하나의 완전한 JSON 응답을 받으려면 streamfalse로 설정하십시오. /api/chat은 또한 toolstool_choice를 허용합니다. VLM 요청의 경우 각 /api/chat 메시지의 images 배열 또는 최상위 images 배열에 원시 base64 이미지 문자열을 넣으십시오. 이는 /api/generate에 사용됩니다.

추론 모델

Qwen3 또는 Gemma 4 E2B/E4B와 같은 추론 모델은 최종 답변과 별도로 추론 결과를 반환할 수 있습니다. OpenAI와 호환되는 요청의 경우 최상위 수준에서 enable_thinking을 설정하십시오.

curl http://<modalix-ip>:9998/v1/chat/completions \
-H "Content-Type: application/json" \
-d '{
"model": "llm",
"messages": [
{"role": "user", "content": "Which is larger: 9.11 or 9.9? Explain."}
],
"enable_thinking": true,
"max_tokens": 256,
"stream": false
}'

스트리밍되지 않은 응답은 추론 내용을 choices[0].message.reasoning_content에, 최종 답변을 choices[0].message.content에 배치합니다. 스트리밍을 사용하는 경우(stream: true), 각 부분은 choices[0].delta.reasoning_contentchoices[0].delta.content를 사용합니다.

Ollama와 호환되는 요청을 하려면 think를 사용하세요.

curl http://<modalix-ip>:9998/api/chat \
-H "Content-Type: application/json" \
-d '{
"model": "llm",
"messages": [
{"role": "user", "content": "Which is larger: 9.11 or 9.9? Explain."}
],
"think": true,
"options": {"num_predict": 256},
"stream": false
}'

/api/chatmessage.thinking에 추론 과정을, message.content에 최종 답변을 반환합니다. /api/generate는 동일한 최상위 think 필드를 입력받아, 최상위 thinking에 추론 과정을, response에 답변을 반환합니다.

추론 필드는 비어 있을 경우 생략됩니다. 이는 출력 전용 필드이며, 입력되는 reasoning_content 또는 thinking 필드는 대화 기록으로 다시 재생되지 않습니다.

오디오 요청

오디오 엔드포인트는 제공되는 ASR 모델 이름과 오디오 파일을 포함하는 멀티파트 폼 데이터를 받습니다. language는 기본적으로 auto로 설정되고, stream은 기본적으로 false로 설정됩니다.

curl http://<modalix-ip>:9998/v1/audio/transcriptions \
-F model=asr \
-F file=@speech.wav \
-F language=auto \
-F stream=false

동일한 형식의 필드를 사용하여 /v1/audio/translations를 활용하여 음성을 영어로 번역합니다. 두 가지 방식 모두 stream=true를 지원합니다. 결과에는 생성된 텍스트, 감지된 언어, 그리고 모델이 제공하는 경우 ASR 신뢰도 정보가 포함됩니다.

스트리밍 및 취소

OpenAI 호환 챗, 완료, 오디오 스트림은 text/event-stream을 사용하고, data: 이벤트를 발생시키며, data: [DONE]으로 완료됩니다. Ollama 호환 스트림은 application/x-ndjson을 사용합니다. 스트리밍 응답에는 사용 가능한 경우 ttfttps가 포함됩니다. 생성된 토큰 수는 OpenAI 호환 스트림에서는 generated_tokens을, Ollama 호환 스트림에서는 eval_count를 사용합니다.

현재 활성 상태인 특정 모델의 스트림을 취소합니다.

curl http://<modalix-ip>:9998/stop \
-H "Content-Type: application/json" \
-d '{"model": "llm"}'

model을 생략하면 활성 상태의 모든 스트림이 취소됩니다. 이 HTTP 경로를 통해 활성 스트리밍 생성이 취소되지만, 동기 요청이 취소되거나 서버 프로세스가 중지되지는 않습니다. server.start()로 시작된 서버를 중지하려면 해당 서버를 소유한 애플리케이션에서 server.stop()을 호출합니다.

LoRA 어댑터 전환

LLiMa의 LORA_BRANCH 모드로 컴파일된 모델의 경우, 모델의 npy_files/<adapter-name> 디렉터리에서 어댑터를 활성화합니다.

curl http://<modalix-ip>:9998/set_lora \
-H "Content-Type: application/json" \
-d '{"model":"llm","name":"customer-adapter"}'

다음 명령어를 사용하여 기본 가중치로 되돌립니다.

curl http://<modalix-ip>:9998/unset_lora \
-H "Content-Type: application/json" \
-d '{"model":"llm"}'

model 필드는 정확히 하나의 텍스트 또는 비전-언어 모델이 등록된 경우에만 생략할 수 있습니다. 모델 전환은 해당 모델에 대한 활성 요청이 완료될 때까지 기다리며, 다른 서비스 중인 모델에는 영향을 미치지 않습니다. 어댑터 이름은 단일 디렉터리 이름이어야 하며, 경로 및 디렉터리 탐색은 허용되지 않습니다. ASR, 추론 디코딩 패키지 또는 영구적으로 병합된 LORA_MERGED 가중치에 대해서는 동적 전환이 지원되지 않습니다.

오류

명시적인 엔드포인트 검증 실패는 HTTP 400을 반환하며, 여기에는 누락된 model, 잘못된 도구 구성, 호환되지 않는 모델 기능 또는 누락된 다중 부분 오디오 file가 포함됩니다. 알 수 없는 서비스 중인 모델은 HTTP 404를 반환합니다. 응답 스트림이 설정되기 전에 요청을 구문 분석하거나 모델을 실행하는 동안 발생하는 예외는 HTTP 500을 반환합니다.

스트리밍이 시작되기 전에 오류는 JSON 형식의 {"error":{"message":"...","type":"invalid_request_error"}}를 사용하여 반환됩니다. 스트리밍 응답이 시작된 후에는 HTTP 상태를 변경하는 대신 엔드포인트의 SSE 또는 NDJSON 스트림에서 오류가 발생합니다.

다음 단계