Tensor LabsTENSORLABS

Build a Screenshot-to-React Service with Kimi K2.7 Code HighSpeed

In late June 2026, Moonshot AI added a HighSpeed serving tier for Kimi K2.7 Code, the one-trillion-parameter open-weight coding model it published under a Modified MIT license on June 12.

July 6, 20268 min read12 sectionsBy Ahmed Abdullah
Build a Screenshot-to-React Service with Kimi K2.7 Code HighSpeed

Introduction

In late June 2026, Moonshot AI added a HighSpeed serving tier for Kimi K2.7 Code, the one-trillion-parameter open-weight coding model it published under a Modified MIT license on June 12. Same weights, faster serving: roughly 180 output tokens per second, up to 260 on short contexts. That speed unlocks a workflow that was previously too sluggish to bother with: turning UI screenshots into working React components quickly enough that a developer keeps iterating instead of giving up. In this tutorial you build a service that accepts a screenshot and returns a typed React component styled with Tailwind CSS, plus a refinement endpoint for the second and third pass.

What Kimi K2.7 Code HighSpeed is

Moonshot AI built Kimi K2.7 Code as a Mixture-of-Experts system: one trillion total parameters with 32 billion active per token, a 256K context window, and a 400-million-parameter MoonViT vision encoder that lets the model read images natively. HighSpeed, served under the model id kimi-k2.7-code-highspeed, runs the identical weights on a faster serving stack.

For a chat assistant, 180 tokens per second is a nice-to-have. For component generation it decides whether the tool gets used. A realistic 150-line component is around 2,500 output tokens. At the 40 tokens per second typical of large models under load, each attempt costs you a full minute of staring. At 180, it lands in about 14 seconds. Nobody refines a design over a one-minute loop; everybody refines over a 14-second one.

How the model reads your screenshot

There is no OCR step and no DOM inspection. MoonViT encodes the screenshot into tokens that flow through the same transformer as your text prompt, so the model attends over visual layout the same way it attends over code. It sees that a card grid is a grid, that a sidebar is fixed, that a button row is right-aligned.

Understand what that buys you and what it does not. Structure is reliable: hierarchy, grouping, alignment, and component boundaries come out right. Exact values are estimates: a #1a56db button may come back as blue-600, and 18 pixels of padding may come back as p-4. You are getting a faithful skeleton with approximate skin, which is exactly the right starting point for a component you were going to adjust anyway.

Set up the project

Two packages and one key. The HighSpeed endpoint is OpenAI-compatible, so the stock openai client is the only transport this build needs.

code
python -m venv venv && source venv/bin/activate
pip install openai fastapi uvicorn python-multipart
export MOONSHOT_API_KEY="sk-your-key-from-platform-moonshot-ai"

The key comes from the Moonshot AI platform console. Keep it in the environment. The Modified MIT license means you can later self-host the weights and repoint one constant, which is the quiet advantage of building against an open model.

Send the screenshot to the model

Images travel as base64 data URLs inside the message content. One content part carries the image, one carries the instruction.

python
import os
import base64
from openai import OpenAI
MOONSHOT_BASE_URL = "https://api.moonshot.ai/v1"
MODEL_ID = "kimi-k2.7-code-highspeed"
moonshot = OpenAI(
base_url=MOONSHOT_BASE_URL,
api_key=os.getenv("MOONSHOT_API_KEY"),
)
def image_to_data_url(image_bytes: bytes, mime: str = "image/png") -> str:
encoded = base64.b64encode(image_bytes).decode("utf-8")
return f"data:{mime};base64,{encoded}"
def generate_component(image_bytes: bytes, hint: str = "") -> str:
completion = moonshot.chat.completions.create(
model=MODEL_ID,
temperature=0.2,
messages=[
{"role": "system", "content": COMPONENT_CONTRACT},
{"role": "user", "content": [
{"type": "image_url",
"image_url": {"url": image_to_data_url(image_bytes)}},
{"type": "text",
"text": f"Reproduce this UI as one React component. {hint}"},
]},
],
)
return strip_fences(completion.choices[0].message.content)

Temperature 0.2 rather than 0 is a deliberate choice for generation work: pure greedy decoding on long components occasionally locks into repetitive class strings, and a small amount of sampling avoids it without making output erratic.

Pin the output contract

The difference between a demo and a service is that a service never wonders what came back. The system prompt is a contract, and the model honors it far more reliably when the contract is short and testable.

python
COMPONENT_CONTRACT = """You convert UI screenshots into React components.
Rules:
- Return EXACTLY ONE .tsx file, nothing else. No explanations.
- TypeScript function component with a typed Props interface.
- Tailwind CSS classes only. No CSS files, no styled-components.
- No component libraries. Plain JSX elements only.
- Use semantic HTML: nav, main, section, button where they apply.
- Placeholder images come from https://placehold.co with the right sizes."""
def strip_fences(text: str) -> str:
cleaned = text.strip()
if cleaned.startswith("```"):
first_newline = cleaned.index("\n")
cleaned = cleaned[first_newline + 1:]
cleaned = cleaned.rsplit("```", 1)[0]
return cleaned.strip()

strip_fences exists because a coding model wraps output in markdown fences whenever it thinks it is being helpful, and a .tsx file starting with three backticks will not compile. Guard for it once, at the boundary.

Wrap it in an endpoint

FastAPI turns the function into something your tooling can call: a Storybook script, a Slack bot, an internal CLI.

python
from fastapi import FastAPI, File, HTTPException, UploadFile
app = FastAPI(title="screenshot-to-react")
MAX_UPLOAD_BYTES = 4 * 1024 * 1024
@app.post("/components")
async def create_component(file: UploadFile = File(...), hint: str = ""):
if file.content_type not in ("image/png", "image/jpeg"):
raise HTTPException(422, "Upload a PNG or JPEG screenshot.")
image_bytes = await file.read() 
if len(image_bytes) > MAX_UPLOAD_BYTES:
raise HTTPException(413, "Screenshot larger than 4 MB. Crop it.")
code = generate_component(image_bytes, hint)
return {"filename": "GeneratedComponent.tsx", "code": code}

The size cap is not bureaucracy. A 4K screenshot burns vision tokens on pixels the model does not need, costs more, and generates slower. Crop to the region you want built and the results improve while the bill shrinks.

Add the refinement turn

The first pass gets you 80 percent. The valuable endpoint is the one that takes the generated code plus a correction and returns the adjusted component, because it keeps naming and structure stable instead of rerolling the dice.

python
@app.post("/components/refine")
async def refine_component(payload: dict):
code, instruction = payload["code"], payload["instruction"]
completion = moonshot.chat.completions.create(
model=MODEL_ID,
temperature=0.2,
messages=[
{"role": "system", "content": COMPONENT_CONTRACT},
{"role": "assistant", "content": code},
{"role": "user",
"content": f"Modify the component: {instruction}. "
"Return the complete updated file."},
],
)
return {"code": strip_fences(completion.choices[0].message.content)}

At HighSpeed rates this round trip is fast enough to sit inside a designer's feedback session. "Make the sidebar collapsible" comes back before the conversation moves on, which is the whole reason to pay for the faster tier.

Kimi K2.7 Code HighSpeed vs standard K2.7 Code

Same model, two serving tiers, two different jobs.

DimensionK2.7 Code HighSpeedK2.7 Code (standard)
Model idkimi-k2.7-code-highspeedkimi-k2.7-code
Output speed~180 tok/s, 260 short-contextStandard serving speed
WeightsIdentical, Modified MITIdentical, Modified MIT
Context256K256K
Right forInteractive loops, UI iterationBatch jobs, CI tasks, overnight runs

The decision rule is simple: if a human is waiting on the response, HighSpeed. If a queue is waiting, standard, and spend the difference on more runs.

Where it falls short

A screenshot is one frozen state, so the model cannot know your hover styles, breakpoints, loading states, or what the empty version of the screen looks like. It approximates spacing and color instead of matching your design tokens, and it will cheerfully invent an icon set you do not use. Accessibility comes out partial: semantic tags appear because the contract demands them, but focus management and aria wiring remain your job. And a service that turns any screenshot into a working component is also a machine for cloning other people's interfaces, and someone on your team will have tried that before the week is out (the demo GIF never shows that part). Set the norms before the tool does.

Full working example

python
import os
import base64
from fastapi import FastAPI, File, HTTPException, UploadFile
from openai import OpenAI
MOONSHOT_BASE_URL = "https://api.moonshot.ai/v1"
MODEL_ID = "kimi-k2.7-code-highspeed"
MAX_UPLOAD_BYTES = 4 * 1024 * 1024
COMPONENT_CONTRACT = """You convert UI screenshots into React components.
Rules:
- Return EXACTLY ONE .tsx file, nothing else. No explanations.
- TypeScript function component with a typed Props interface.
- Tailwind CSS classes only. No CSS files, no styled-components.
- No component libraries. Plain JSX elements only.
- Use semantic HTML: nav, main, section, button where they apply.
- Placeholder images come from https://placehold.co with the right sizes."""
moonshot = OpenAI(
base_url=MOONSHOT_BASE_URL,
api_key=os.getenv("MOONSHOT_API_KEY"),
)
app = FastAPI(title="screenshot-to-react")
def image_to_data_url(image_bytes: bytes, mime: str = "image/png") -> str:
encoded = base64.b64encode(image_bytes).decode("utf-8")
return f"data:{mime};base64,{encoded}"
def strip_fences(text: str) -> str:
cleaned = text.strip()
if cleaned.startswith("```"):
cleaned = cleaned[cleaned.index("\n") + 1:]
cleaned = cleaned.rsplit("```", 1)[0]
return cleaned.strip()
def call_kimi(messages: list) -> str:
completion = moonshot.chat.completions.create(
model=MODEL_ID, temperature=0.2, messages=messages,
)
return strip_fences(completion.choices[0].message.content)
@app.post("/components")
async def create_component(file: UploadFile = File(...), hint: str = ""):
if file.content_type not in ("image/png", "image/jpeg"):
raise HTTPException(422, "Upload a PNG or JPEG screenshot.")
image_bytes = await file.read()
if len(image_bytes) > MAX_UPLOAD_BYTES:
raise HTTPException(413, "Screenshot larger than 4 MB. Crop it.")
code = call_kimi([
{"role": "system", "content": COMPONENT_CONTRACT},
{"role": "user", "content": [
{"type": "image_url",
"image_url": {"url": image_to_data_url(image_bytes)}},
{"type": "text",
"text": f"Reproduce this UI as one React component. {hint}"},
]},
])
return {"filename": "GeneratedComponent.tsx", "code": code}
@app.post("/components/refine")
async def refine_component(payload: dict):
code = call_kimi([
{"role": "system", "content": COMPONENT_CONTRACT},
{"role": "assistant", "content": payload["code"]},
{"role": "user",
"content": f"Modify the component: {payload['instruction']}. "
"Return the complete updated file."},
])
return {"code": code}

Run it with uvicorn main:app --port 8000, post a screenshot to /components, and paste the result into your repo.

When to reach for it

Point it at the surfaces where speed beats polish: internal tools, admin panels, prototypes that need to exist by the afternoon demo, and marketing pages a designer will restyle anyway. Skip it for design-system-critical product UI, where the real work is matching your tokens and interaction patterns, not producing plausible JSX. The pattern to build toward is wiring /components into your Storybook workflow so every generated component lands as a story a human reviews. The model produces the first draft at 180 tokens per second. Deciding whether it belongs in your product is still the slow part, and it should be.