DEV Community

shashank ms
shashank ms

Posted on

Integrating LLM with Mobile Applications

We are building a FastAPI backend that powers a mobile journaling app with AI-generated mood insights and daily summaries. The service forwards entries to Oxlo.ai, where flat per-request pricing keeps costs predictable even when users write long reflections. This pattern works for any mobile client, whether React Native, Flutter, or native iOS and Android.

What you'll need

  • Python 3.10 or newer
  • pip install openai fastapi uvicorn python-multipart
  • An Oxlo.ai API key from https://portal.oxlo.ai

1. Scaffold the API and configure the Oxlo.ai client

Create main.py and instantiate the OpenAI client pointing at Oxlo.ai. I use Llama 3.3 70B as the default because it handles open-ended conversational tasks reliably.

from openai import OpenAI
from fastapi import FastAPI
import os

app = FastAPI()

client = OpenAI(
    base_url="https://api.oxlo.ai/v1",
    api_key=os.environ.get("OXLO_API_KEY")
)

DEFAULT_MODEL = "llama-3.3-70b"

2. Craft the system prompt

Keep the prompt in its own variable so product teams can tune it without touching route logic. It asks for structured JSON that the mobile UI can parse directly.

SYSTEM_PROMPT = """You are a thoughtful journaling assistant inside a mobile app.
Respond in valid JSON with these keys: summary, mood, reflection.
- summary: 2 sentences summarizing the entry.
- mood: a single dominant mood label.
- reflection: one gentle, actionable suggestion.
Keep total output concise for mobile screens.
Do not provide medical or therapeutic diagnoses.
"""

3. Build the chat endpoint

Define a Pydantic model for incoming entries and POST it to /journal. We enable JSON mode so the response is machine-readable on the client side.

from pydantic import BaseModel
from fastapi.responses import JSONResponse

class JournalEntry(BaseModel):
    user_id: str
    text: str

@app.post("/journal")
async def analyze_entry(entry: JournalEntry):
    response = client.chat.completions.create(
        model=DEFAULT_MODEL,
        messages=[
            {"role": "system", "content": SYSTEM_PROMPT},
            {"role": "user", "content": entry.text},
        ],
        response_format={"type": "json_object"},
    )

    content = response.choices[0].message.content
    return JSONResponse(content={"user_id": entry.user_id, "analysis": content})

4. Add vision support for photo entries

Mobile users often attach photos. We add a /journal/vision route that accepts an image upload and forwards it to Kimi K2.6. On Oxlo.ai, this still counts as one flat request, so image plus text does not change the unit cost.

from fastapi import File, UploadFile
import base64

VISION_MODEL = "kimi-k2.6"

@app.post("/journal/vision")
async def analyze_photo_entry(text: str, photo: UploadFile = File(...)):
    image_bytes = await photo.read()
    b64_image = base64.b64encode(image_bytes).decode("utf-8")
    data_url = f"data:{photo.content_type};base64,{b64_image}"

    response = client.chat.completions.create(
        model=VISION_MODEL,
        messages=[
            {"role": "system", "content": SYSTEM_PROMPT},
            {
                "role": "user",
                "content": [
                    {"type": "text", "text": text},
                    {"type": "image_url", "image_url": {"url": data_url}},
                ],
            },
        ],
    )

    return JSONResponse(content={"analysis": response.choices[0].message.content})

5. Stream responses to the mobile client

For the text-only endpoint, streaming improves perceived speed on cellular networks. We switch to a generator and yield tokens as Oxlo.ai produces them.

from fastapi.responses import StreamingResponse

@app.post("/journal/stream")
async def stream_analysis(entry: JournalEntry):
    stream = client.chat.completions.create(
        model=DEFAULT_MODEL,
        messages=[
            {"role": "system", "content": SYSTEM_PROMPT},
            {"role": "user", "content": entry.text},
        ],
        stream=True,
    )

    def event_generator():
        for chunk in stream:
            delta = chunk.choices[0].delta.content
            if delta:
                yield delta

    return StreamingResponse(event_generator(), media_type="text/plain")

Run it

Start the server:

uvicorn main:app --reload

Test the text endpoint with curl:

curl -X POST http://localhost:8000/journal \
  -H "Content-Type: application/json" \
  -d '{"user_id":"user-42","text":"Felt overwhelmed by back-to-back meetings today, but the evening walk by the lake calmed me down."}'

Expected response:

{
  "user_id": "user-42",
  "analysis": "{\"summary\":\"A stressful workday was diffused by a calming evening walk.\",\"mood\":\"Stressed to Calm\",\"reflection\":\"Consider blocking 10 minutes of buffer time between meetings tomorrow.\"}"
}

Wrap-up

Wire this backend into your mobile client using React Native's fetch or Flutter's http package. If you later need deeper reasoning or agentic workflows, swap Llama 3.3 70B for Qwen 3 32B or DeepSeek V3.2 on Oxlo.ai without changing integration code. See https://oxlo.ai/pricing for plan details.

Top comments (0)