AI School Pakistan
← Applied AI Builder0 of 18 complete
LessonCourse overview

Module 1 · Technical Foundations · 45 min

Lab: Send, Inspect, and Log Your First Model Request

Lesson 3 of 18

Is lesson ke baad tum

record what actually ran, check format and meaning separately, and keep provider identifiers and usage distinct from local fixtures.

In this lesson

Run one small classification experiment and keep a metadata-only record. Start in fixture mode, which needs Node but no account, SDK, or paid request. The optional live route needs the official OpenAI SDK, an authorized API project with available quota/credits or billing, a permitted model, and a protected API key. A ChatGPT subscription does not establish API access or an API spending allowance.

Prepare the Lab

Create a new folder, run npm init -y, and create .gitignore before any secret or log:

prompt to try4 lines
.env
request-log.jsonl
node_modules/
*.log

Create .env.example with names and placeholders only:

prompt to try4 lines
LAB_MODE=fixture
LAB_CASE=wrong-colour
OPENAI_API_KEY=set_only_for_authorized_live_mode
OPENAI_MODEL=choose_an_available_text_model_for_your_API_project

The script reads process environment variables. Merely creating .env.example does not load them or make a request. The defaults below select fixture mode; downloads and API-account setup are separate from the core activity.

Worked Example

Save this as request.mjs. Fixture outputs are deliberately supplied answers, not measured model results:

javascript59 lines
import { appendFile } from "node:fs/promises";

const fixtures = {
  "wrong-colour": {
    message: "My demo order arrived with the wrong colour.",
    output: "category: product issue\nreason: The delivered colour differs from the order."
  },
  "tracking": {
    message: "My demo order tracking link has not updated.",
    output: "category: tracking issue\nreason: The tracking information has not updated."
  }
};

try {
  const mode = process.env.LAB_MODE ?? "fixture";
  if (!["fixture", "live"].includes(mode)) throw new Error("Invalid LAB_MODE");
  const caseId = process.env.LAB_CASE ?? "wrong-colour";
  if (!Object.hasOwn(fixtures, caseId)) throw new Error("Unknown LAB_CASE");
  const sample = fixtures[caseId];
  const prompt = "Classify this synthetic support message. Return two lines only: "
    + "category and one-sentence reason. Do not invent policy or actions. Message: "
    + sample.message;
  if (prompt.length > 500) throw new Error("Input cap exceeded");

  const configuredModel = mode === "live" ? process.env.OPENAI_MODEL : null;
  if (mode === "live" && (!process.env.OPENAI_API_KEY || !configuredModel)) {
    throw new Error("Live mode needs OPENAI_API_KEY and OPENAI_MODEL");
  }
  const startedAt = new Date().toISOString();
  const startMs = performance.now();
  let response;
  if (mode === "fixture") {
    response = {status:"completed", output_text:sample.output,
      id:null, model:null, usage:null};
  } else {
    const {default: OpenAI} = await import("openai");
    const client = new OpenAI({apiKey:process.env.OPENAI_API_KEY,
      maxRetries:0, timeout:30_000});
    response = await client.responses.create({model:configuredModel,
      input:prompt, max_output_tokens:1024, store:false});
  }
  if (response.status !== "completed") throw new Error("Response incomplete");
  const output = response.output_text?.trim() ?? "";
  const lineCount = output.split(/\r?\n/).filter(Boolean).length;
  if (!output || lineCount !== 2) throw new Error("Expected two nonempty lines");
  const record = {timestamp:startedAt, mode, case_id:caseId,
    response_id:response.id ?? null, configured_model:configuredModel,
    returned_model:response.model ?? null,
    elapsed_ms:Math.round(performance.now()-startMs), usage:response.usage ?? null,
    output_review:{non_empty:true,line_count:lineCount}};
  console.log(output); // only synthetic text belongs in this lab
  await appendFile("request-log.jsonl", JSON.stringify(record)+"\n", "utf8");
  console.log(record);
} catch (error) {
  console.error({category:Number.isInteger(error?.status)
    ? "api_error" : "local_or_transport_error",
    status:Number.isInteger(error?.status) ? error.status : null});
  process.exitCode = 1;
}

Run node request.mjs. Expect the two supplied wrong-colour lines and a record with mode fixture, case_id wrong-colour, and null provider ID/model/usage fields. The measured elapsed time is local fixture-processing time, not model latency. JSON Lines means one complete JSON object per line in the log.

The code enforces nonempty output and exactly two lines. It does not judge meaning. Check that the reason follows only from the supplied message and does not invent a refund, policy, customer identity, or delivery date. A completed API response can still fail this semantic check.

Hands-On Exercise

In PowerShell set $env:LAB_CASE="tracking" and rerun. On macOS/Linux use LAB_CASE=tracking node request.mjs. Inspect the two log records and write which one business detail changed.

Check: the second output concerns tracking, not a product colour. Both records say fixture, retain null provider usage, and omit the full prompt and output from the JSONL file. Do not rename them live results or invent response IDs. Your short notes should record the meaning check as well as the format check.

Change LAB_CASE to unknown and run once. Expect a nonzero exit with the safe local_or_transport_error category and no appended success record. Restore a valid case afterward. The error category is intentionally broad; diagnose configuration locally without printing secret values or an entire SDK error object.

Optional Live Request

After reviewing current model access, pricing, data controls, and a small spending limit, install the SDK with npm install openai. Set the key securely in your local process using official setup guidance and set OPENAI_MODEL to an available compatible text model. Then set LAB_MODE to live and run one synthetic case. Never place the actual key in source, a submitted runbook, shell screenshots, or browser code.

The live path disables automatic SDK retries and sets a 30-second client timeout. If the request times out, it may still consume provider work; do not infer zero cost or retry without a budget decision. The 1,024-token output cap can include reasoning tokens, so an incomplete result needs a deliberate review of its cause. store:false controls Responses application storage; it does not independently enable Zero Data Retention or remove every provider log.

Record the actual response ID, configured/returned model, elapsed time, usage, and human meaning check. Keep request-log.jsonl out of Git. A fixture pass does not certify the model; a single live success also does not establish general reliability.

Completion Rubric

0/4

Sources

Practice quiz

Check yourself

4 questions · practise what you just learned

0/4
  1. 1 / 4 · diagnose

    The first fixture record has null response_id, returned_model, and usage. What should the learner do?

Apna kaam check karo.

Practice complete ki? Result ko example aur lesson ke checks se compare karo.

This is your own completion record, not a graded assessment.

Kuch samajh nahi aaya? Ask a question →