Scaleway Generative APIs
Use Context212 search as the retrieval layer for models hosted on Scaleway.
Scaleway's Generative APIs expose hosted models (Llama, Mistral, and others) through an OpenAI-compatible endpoint. Pair them with Context212 search to build RAG pipelines where the retrieval stays on Context212's infrastructure and the generation runs on Scaleway.
The flow is:
- Search Context212 for the passages most relevant to the user's question.
- Pack those passages into the model's context window.
- Call the Scaleway model to generate an answer grounded in the retrieved content.
Prerequisites
- A
C212_API_KEY, available in the Console → API Keys section. - A Scaleway API key with access to Generative APIs, available in the Scaleway console under IAM → API Keys.
- At least one workspace with indexed documents on Context212.
Installation
npm install openaiThe openai package is used here only for its client; Scaleway's endpoint is fully compatible with it.
Full example
import OpenAI from "openai";
const C212_API_KEY = process.env.C212_API_KEY!;
const SCALEWAY_API_KEY = process.env.SCALEWAY_API_KEY!;
const scaleway = new OpenAI({
baseURL: "https://api.scaleway.ai/v1",
apiKey: SCALEWAY_API_KEY,
});
async function search(
query: string,
workspaceId?: number[],
maxResults = 5,
): Promise<Record<string, unknown>[]> {
const payload: Record<string, unknown> = { query, max_results: maxResults };
if (workspaceId) payload.workspace_id = workspaceId;
const response = await fetch("https://api.context212.com/api/v1/search", {
method: "POST",
headers: {
Authorization: `Bearer ${C212_API_KEY}`,
"Content-Type": "application/json",
},
body: JSON.stringify(payload),
});
if (!response.ok) throw new Error(`Search failed: ${response.status}`);
return (await response.json()).results;
}
async function answer(
question: string,
workspaceId?: number[],
model = "llama-3.3-70b-instruct",
): Promise<string> {
const results = await search(question, workspaceId);
const context = results
.filter((r: any) => r.content)
.map(
(r: any) =>
`[${r.source.filename}, p.${r.source.page_start}]\n${r.content}`,
)
.join("\n\n");
const completion = await scaleway.chat.completions.create({
model,
messages: [
{
role: "system",
content:
"You are a helpful assistant. Answer the user's question using only " +
"the provided context. If the context does not contain enough information, " +
"say so.\n\nContext:\n" +
context,
},
{ role: "user", content: question },
],
});
return completion.choices[0].message.content ?? "";
}
console.log(await answer("What is our data retention policy?"));Context212 search as a tool
Instead of always searching before calling the model, you can expose Context212 search as a tool and let the model decide when to call it. The model issues an context212_search tool call when it needs context; your code executes the search and feeds the results back; the model then produces a final answer.
import OpenAI from "openai";
const C212_API_KEY = process.env.C212_API_KEY!;
const SCALEWAY_API_KEY = process.env.SCALEWAY_API_KEY!;
const scaleway = new OpenAI({
baseURL: "https://api.scaleway.ai/v1",
apiKey: SCALEWAY_API_KEY,
});
const SEARCH_TOOL = {
type: "function" as const,
function: {
name: "context212_search",
description:
"Search the company knowledge base for passages relevant to a query. " +
"Returns ranked excerpts with their source filename and page numbers.",
parameters: {
type: "object",
properties: {
query: {
type: "string",
description: "Natural-language search query.",
},
max_results: {
type: "integer",
description: "Number of passages to return (1–50, default 5).",
default: 5,
},
},
required: ["query"],
},
},
};
async function runSearch(query: string, maxResults = 5): Promise<string> {
const response = await fetch("https://api.context212.com/api/v1/search", {
method: "POST",
headers: {
Authorization: `Bearer ${C212_API_KEY}`,
"Content-Type": "application/json",
},
body: JSON.stringify({ query, max_results: maxResults }),
});
if (!response.ok) throw new Error(`Search failed: ${response.status}`);
const results = (await response.json()).results;
const passages = results
.filter((r: any) => r.content)
.map(
(r: any) =>
`[${r.source.filename}, p.${r.source.page_start}]\n${r.content}`,
);
return passages.length ? passages.join("\n\n") : "No results found.";
}
async function answer(
question: string,
model = "llama-3.3-70b-instruct",
): Promise<string> {
const messages: OpenAI.Chat.ChatCompletionMessageParam[] = [
{ role: "user", content: question },
];
while (true) {
const completion = await scaleway.chat.completions.create({
model,
tools: [SEARCH_TOOL],
messages,
});
const choice = completion.choices[0];
if (choice.finish_reason === "tool_calls") {
messages.push(choice.message);
for (const call of choice.message.tool_calls ?? []) {
const args = JSON.parse(call.function.arguments);
const result = await runSearch(args.query, args.max_results ?? 5);
messages.push({
role: "tool",
tool_call_id: call.id,
content: result,
});
}
} else {
return choice.message.content ?? "";
}
}
}
console.log(await answer("What is our data retention policy?"));The loop handles the case where the model issues multiple search calls in sequence before producing a final answer.
Scoping retrieval to a workspace
Pass workspace_id to limit search to a specific workspace. This is useful in multi-tenant products where each customer's data lives in a dedicated workspace.
await answer("Summarize the onboarding checklist", [42]);Choosing a model
Scaleway's catalog includes several hosted models. Pass the model name to the model parameter:
| Model | Notes |
|---|---|
llama-3.3-70b-instruct | Strong reasoning, good default choice |
llama-3.1-8b-instruct | Faster and cheaper, suitable for simpler queries |
mistral-nemo-instruct-2407 | Compact Mistral, low latency |
mixtral-8x7b-instruct-v0.1 | MoE model, good for longer contexts |
Check the Scaleway documentation for the current model list and regional availability.
Streaming responses
Scaleway's endpoint supports streaming. Enable it by passing stream: true and iterating over the response:
const stream = await scaleway.chat.completions.create({
model: "llama-3.3-70b-instruct",
messages: [/* ... */],
stream: true,
});
for await (const chunk of stream) {
const delta = chunk.choices[0].delta.content;
if (delta) process.stdout.write(delta);
}