Eagle233-Blog

[Interview] SAP Student Training & Rotation Program(VT/STAR) Intern (Application Engineer) - 2028 Graduates (455960) Online Assessments


Categories Interview
Tags

3k Words   |   18 Minutes

Basic Info

  • Platform: Codility
  • Early Talent Backend Developer: 3 tasks, 95 minutes
  • AI & Agentic: 2 tasks, 80 minutes

Early Talent Backend Developer

Task 1 · Dockerfile

Your team is developing a web application in Node.js and needs to build a lightweight Docker image for reusability. Create a Dockerfile to containerize the project. The image must be standalone, so must contain all project files and required dependencies.

Your task

Create a Dockerfile according to the following expectations:

  • Use the official node Docker image from the Docker Hub;
  • Node.js version can be configured with the NODE_VERSION build argument and defaults to 13;
  • A lightweight Alpine version of the Node.js image is used, so when the version is set to 13 the base image should be node:13-alpine;
  • To make sure project files are both available and run from /usr/src/app, change the working directory to this specific path;
  • Copy all project files from the local ./project directory to the current working directory;
  • Install all dependencies with the npm install command;
  • Expose port 8080, as our project is a web server listening on this specific TCP port;
  • The application keeps its state on disk under /var/lib/project, so make sure this directory is set as a volume;
  • Set node server.js as a default command to execute when starting a container, leaving the entry point empty.

Task 2 · cityWeather

Write a function named cityWeather that returns an object representing the weather in a given city. The function should fetch the data from the mocked API.

Requirements

Implement the cityWeather function to satisfy the following requirements:

  1. The function has one input parameter city, which is a string that cannot be empty.

    • Throw the “not a string” message if the city parameter is not a string.
    • Throw the “string is empty” if city is an empty string.
  2. The BASE_URL variable will be imported at the top of your inital file; you need to concatenate the query prefix &q= between the BASE_URL and the city parameter (as the url) in order to fetch the data correctly like so:

    <base url><query prefix><city parameter>

  3. The function should call the mocked API and return an object that contains the weather data for the specified city. The main property of the mocked response contains the data to be returned. The format of the returned object should be as follows:

    {
      "temp": 21,
      "feels_like": 19.54,
      "temp_min": 21,
      "temp_max": 21,
      "pressure": 1026,
      "humidity": 49
    }
  4. If the specified city doesn’t exist in the data, throw the “city not found” error.

    • This message is provided by the mocked-up API and can be sent back using reject (in the promise placed in the initial code with error.data.message parameter).

Assumptions

  • The API function (axios.get(url) or fetch(url)) will handle blank spaces, Unicode characters and lower/upper case values (note that the function will transform all characters to lower case to find a match).

Hints

  • As Axios and Fetch use HTTP requests, the data will be mocked up for the response. This will be done automatically when you make an HTTP GET request (axios.get(url) or fetch(url)); if there is no match - an error will be returned.
  • Use the HTTP request (asynchronously) to be able return an object as expected (the return of the HTTP request is a promise resolving to an object). An example how to do this is given in the initial code file.
  • Don’t forget that blank spaces can be an input.

Async Await

  • You can use async, await. Just make sure you add the async prefix before the function like so:
async function cityWeather(city)
{
    ...
}

Available packages/libraries

To get the data using API calls you may use the packages:

  • Node.js 26
  • axios 1.9.0

Examples

The successful response from the mocked API has the following format:

{
    status: 200,
    data: {
        ...,
        main: {
            temp: 21,
            feels_like: 19.54,
            temp_min: 21,
            temp_max: 21,
            pressure: 1026,
            humidity: 49
        },
        ...
    }
}

The error returned by the API has the following format:

{
    "status": 404,
    "data": {
        "message": "city not found",
        "code": 404
    }
}

Example scenarios:

  1. Case 1: Input is not a string.
    • Examples might be null, undefined, {}, [], or number (0,5,99,etc..).
    • Expected Throw: not a string
  2. Case 2: Input is an empty string.
    • Expected Throw: string is empty
  3. Case 3: Input is a string, but it is a city that’s not found.
    • For Input: cityWeather('Seoul')
    • Expected Throw: city not found (promise reject)
  4. Case 4: Valid city that contains weather data as object properties.
    • For Input: cityWeather('Bogotá')

    • Expected Output:

      {
        "temp": 21,
        "feels_like": 19.54,
        "temp_min": 21,
        "temp_max": 21,
        "pressure": 1026,
        "humidity": 49
      }

Initial code

import { fetch } from "./callMock.js";
import axios from "axios";

import { BASE_URL } from "./baseUrl.js";

function cityWeather(city) {
  // Use console.log within the cityWeather function for debugging.
  // Use the &q= prefix with the city parameter at the end of the URL in order to mock the data correctly.

  return new Promise((resolve, reject) => {
    // Implement the function - call http request
    resolve(object_to_be_returned);
    // Implement the function
  });
}

export { cityWeather };

Task 3 · JsonParser

Convert JSON data into a JVM object using the Gson library.

Please keep in mind that some JSON properties may be null or empty and you should assign default values to them.

Prerequisites

  • You should use Gson for deserialization (library already included in project).
  • You should use Joda-Time for date-time fields (library already included in project).
  • Some JSON properties may be null or empty.
  • You may use @SerializedName annotation.
  • You may register a new TypeAdapter.
  • You may create new inner classes for intermediate JSON processing.
  • Date-time fields are in the ISO8601 standard.

Requirements

  • You should convert results from JSON data to a PostEntity list (provided below).
  • You should convert date-time fields to org.joda.time.DateTime.
  • For null or empty updated_at properties, use created_at, which is always available.
  • For null or empty reactions_count properties, assign 0 in the entity model.

Remember, that by default in POJO model, boxed types (such as Integer) can be resolved to null while parsing and primitive types (such as int) can be resolved to default value (int → 0).

PostEntity is already present in package, you cannot edit this class. For intermediate processing you may create new DTO class.

Example Json data

{
  "results": [
    {
      "id": "098asdpoiuf876-1",
      "author_id": "987asdkjh987s",
      "content": "Maybe it's time to rethink your life choices?",
      "created_at": "2020-05-12T09:17:45.814+0200"
    },
    {
      "id": "098asdpoiuf876-2",
      "author_id": "987asdkjh987d",
      "content": "New DT album is great!",
      "created_at": "2020-05-12T09:17:45.814+0200",
      "updated_at": "2020-05-12T09:17:47.814+0200",
      "reactions_count": 5
    },
    {
      "id": "098asdpoiuf876-3",
      "author_id": "987asdkjh987s",
      "content": "Never fully realised how tricky computers may be",
      "created_at": "2020-05-12T09:18:45.814+0200",
      "reactions_count": null
    },
    {
      "id": "098asdpoiuf876-4",
      "author_id": "987asdkjh987w",
      "content": "from now on, I play only 9-42 strings",
      "created_at": "2020-05-12T09:19:45.814+0200",
      "updated_at": null,
      "reactions_count": 3
    }
  ],
  "count": 4,
  "next": null,
  "previous": null
}

PostEntity content (included in package)

package com.codility;

import org.joda.time.DateTime;

public class PostEntity {
    private final String id;
    private final String authorId;
    private final String content;
    private final DateTime createdAt;
    private final DateTime updatedAt;
    private final int reactionsCount;

    public PostEntity(
            String id,
            String authorId,
            String content,
            DateTime createdAt,
            DateTime updatedAt,
            int reactionsCount
    ) {
        this.id = id;
        this.authorId = authorId;
        this.content = content;
        this.createdAt = createdAt;
        this.updatedAt = updatedAt;
        this.reactionsCount = reactionsCount;
    }

    public String getId() {
        return id;
    }

    public String getAuthorId() {
        return authorId;
    }

    public String getContent() {
        return content;
    }

    public DateTime getCreatedAt() {
        return createdAt;
    }

    public DateTime getUpdatedAt() {
        return updatedAt;
    }

    public int getReactionsCount() {
        return reactionsCount;
    }
}

Environment

  • Java 8
  • Gson 2.8.6

Initial code

package com.codility;

import com.google.gson.Gson;

import java.io.InputStream;
import java.util.List;

public class JsonParser {
    private final Gson gson = new Gson().newBuilder()
            .create();

    public List<PostEntity> parse(InputStream inputStream) {
        throw new RuntimeException("implement task");
    }
}

AI & Agentic

Task 1 · reliable_call

Intro

You are building an AI-powered backend where a language model (LLM) must return structured JSON responses. In production systems, two classes of failures commonly occur:

  1. Transient client failures — network errors, rate limits, or timeouts when calling the model API.
  2. Invalid model responses — the model returns malformed JSON, incomplete output, or additional commentary.

Your goal is to implement a retrying orchestration layer that handles both failure types while preserving observability information for debugging.

Task

Implement the following function in the provided file:

def reliable_call(
    client: Any,
    messages: list[dict],
    validate_fn: Callable[[str], bool],
    max_retries: int = 3,
    max_client_errors: int = 3,
) -> str:

Parameters

client

An object exposing:

client.complete(messages: list[dict]) -> str

Sends the conversation to the language model and returns its response as a string. May raise ClientError to signal a transient failure (timeout, rate limit, network error).

messages

The initial conversation as a list of {"role": ..., "content": ...} dicts.

Your implementation must not modify the caller’s list or any of its elements.

validate_fn

A callable that checks whether the model response is acceptable. It behaves in one of three ways:

  • return True — the response is valid
  • return False — the response is invalid (generic failure)
  • raise ValidationError — the response is invalid with a specific reason

ValidationError carries two attributes:

response: str — the response that failed validation
reason: str   — a human-readable explanation of why it failed

max_retries

Maximum number of validation failures allowed before giving up (default 3). A validation failure occurs when validate_fn returns False or raises ValidationError.

If max_retries=0, LLMRetryError is raised immediately without calling client.complete().

max_client_errors

Maximum number of client failures allowed before giving up (default 3). A client failure occurs when client.complete() raises ClientError.

The two budgets are independent: exhausting one has no effect on the other.

If max_client_errors=0, LLMRetryError is raised immediately without calling client.complete().

Return Value

Return the first response string for which validate_fn returns True.

Failure Behavior

Raise LLMRetryError as soon as either budget is exhausted.

Exception Classes

Three exception classes are pre-defined. Do not redefine them.

  • ClientError — raised by client.complete() to signal a transient failure.
  • ValidationError — may be raised by validate_fn instead of returning False. Attributes: response, reason.
  • LLMRetryError — raised when either the validation or client-error budget is exhausted. Populate its fields as follows:
Field Value
last_response The most recent response that failed validation. Empty string "" if every attempt raised ClientError.
attempts Total number of calls made to client.complete() (validation failures + client errors).
all_responses All responses that failed validation, in order. Responses from ClientError attempts are excluded.
failure_reason "validation" if the validation budget was exhausted. "client_error" if the client error budget was exhausted.

Retry Behavior: Validation Failures

When validate_fn returns False or raises ValidationError, extend the working conversation with exactly two messages in this order:

  1. {"role": "assistant", "content": <the_failed_response>} — echoes the model’s failed output back
  2. {"role": "user", "content": <correction>} — a corrective prompt

The correction message depends on the failure type:

  • validate_fn returns False: use the constant CORRECTION_MESSAGE verbatim
  • validate_fn raises ValidationError: use:
f"That response was invalid: {e.reason}. Please try again with valid JSON."

A ValidationError is a validation failure: it exhausts the max_retries budget and produces failure_reason="validation".

Retry Behavior: Client Errors

When client.complete() raises ClientError, extend the working conversation with exactly one message:

{"role": "user", "content": CLIENT_ERROR_MESSAGE}

Do not append an assistant echo — there is no response to echo back.

Example 1: Second attempt succeeds

client = MockLLM(["bad", "good"])
# returns responses in sequence: first "bad", then "good"
messages = [
    {"role": "system", "content": "You are a helpful assistant."},
    {"role": "user", "content": "Return valid JSON only."},
]
result = reliable_call(client, messages, validate_fn=lambda r: r == "good")
# result = "good"
# client.complete() was called exactly twice
#
# Call 1 receives:
#   [{"role": "system", "content": "You are a helpful assistant."},
#    {"role": "user", "content": "Return valid JSON only."}]
#   → returns "bad" → validate_fn("bad") is False
#
# Call 2 receives:
#   [{"role": "system", "content": "You are a helpful assistant."},
#    {"role": "user", "content": "Return valid JSON only."},
#    {"role": "assistant", "content": "bad"},
#    {"role": "user", "content": "That response was invalid. Please try again with only valid JSON."}]
#   → returns "good" → validate_fn("good") is True → return "good"

Example 2: ValidationError with reason

def strict_validate(response: str) -> bool:
    import json
    try:
        json.loads(response)
        return True
    except json.JSONDecodeError as exc:
        raise ValidationError(response, str(exc))

client = MockLLM(["not json", '{"ok": true}'])  # returns responses in sequence: first "not json", then valid JSON
result = reliable_call(client, [{"role": "user", "content": "Return JSON."}], strict_validate)
# result = '{"ok": true}'
#
# Call 1 receives:
#   [{"role": "user", "content": "Return JSON."}]
#   → returns "not json" → strict_validate raises ValidationError(reason="Expecting value: line 1 column 1 (char 0)")
#
# Call 2 receives:
#   [{"role": "user", "content": "Return JSON."},
#    {"role": "assistant", "content": "not json"},
#    {"role": "user", "content": "That response was invalid: Expecting value: line 1 column 1 (char 0). Please try again with valid JSON."}]
#   → returns '{"ok": true}' → strict_validate returns True → return '{"ok": true}'

Environment

Python 3.12

Initial code

from typing import Callable, Any

CORRECTION_MESSAGE = "That response was invalid. Please try again with only valid JSON."
CLIENT_ERROR_MESSAGE = "The previous attempt failed due to a service error. Please try again."


class ClientError(Exception):
    """Raised by the LLM client on transient failures (network error, rate limit, timeout)."""
    pass


class ValidationError(Exception):
    """Raised by validate_fn when a response fails validation with a specific reason."""

    def __init__(self, response: str, reason: str):
        super().__init__(reason)
        self.response = response
        self.reason = reason


class LLMRetryError(Exception):
    """Raised when either retry budget is exhausted."""

    def __init__(self, message: str, last_response: str, attempts: int, all_responses: list[str], failure_reason: str):
        super().__init__(message)
        self.last_response = last_response
        self.attempts = attempts
        self.all_responses = all_responses
        self.failure_reason = failure_reason  # "validation" or "client_error"


def reliable_call(
    client: Any,
    messages: list[dict],
    validate_fn: Callable[[str], bool],
    max_retries: int = 3,
    max_client_errors: int = 3,
) -> str:
    pass

Task 2 · Smart Support Router

Intro

You work at a fast-growing SaaS company that receives hundreds of customer emails daily. Currently, all emails go to a single support inbox, and human agents must manually read and route each one to the appropriate department:

  • Technical Support - Handles bugs, crashes, errors, and technical issues
  • Billing - Handles invoices, payments, refunds, and subscription questions
  • General Feedback - Handles suggestions, compliments, and general inquiries

This manual process is slow and error-prone. Your task is to build an automated email routing system using LangChain that:

  1. Classifies incoming emails into one of the three categories
  2. Routes each email to the appropriate specialized department handler
  3. Returns a structured response with the classification and routed result

Task

Implement the following function:

def classify_and_route_email(email_content: str, mock_llm_func) -> EmailClassification

Parameters:

  • email_content (str): The content of the customer email
  • mock_llm_func (function): An LLM function that takes a prompt string and returns a response string

Return Value:

An EmailClassification object with:

  • category: One of “Technical Support”, “Billing”, or “General Feedback”
  • confidence: A float between 0 and 1 indicating classification confidence
  • routed_response: A string response from the specialized department handler

Implementation Requirements

Your solution must implement a two-step process:

  1. Classification - Analyze the email and determine its category
  2. Routing & Response - Route to the appropriate department chain and generate a response

Important: The classification step returns an EmailClassification object where routed_response is initially empty (""). This is only an intermediate result. You must then:

  1. Use the category field to route to the appropriate department chain
  2. Call the mock LLM again with a department-specific prompt
  3. Update the routed_response field with the department’s response
  4. Return the complete EmailClassification object

The final returned object must have both the classification (category, confidence) AND the department response (routed_response) populated.

Required LangChain Components:

  • A prompt template class from langchain_core.prompts (typically PromptTemplate) to create structured prompts for classification and department responses
  • PydanticOutputParser - Parse LLM output into EmailClassification objects
  • RunnableBranch - Route to department-specific chains based on category
  • RunnableLambda - Wrap department handler functions for use in RunnableBranch

These components should be used instead of plain if/elif/else routing or manual string parsing.

Prompt Requirements

Your prompts must follow specific structures to work with the mock LLM:

Classification Prompt:

  • Must contain the word “classify”
  • Must include the email content
  • Must include parser.get_format_instructions() for structured JSON output

Department Response Prompts:

Each department chain must include its role phrase and the email content:

  • Technical Support - “You are a technical support specialist”
  • Billing - “You are a billing specialist”
  • General Feedback - “You are a customer service representative”

You do not need advanced prompt engineering; as long as your prompts follow the structure above and include the email content, the tests focus on whether your classification and routing behavior are correct.

Example 1: Technical Support Email

Input:

email = "My application keeps crashing when I try to login. Getting error code 500."

Expected Output:

EmailClassification(
    category="Technical Support",
    confidence=0.9,
    routed_response="Our technical team will investigate this issue and provide a solution within 24 hours."
)

Example 2: Billing Email

Input:

email = "I was charged twice for my subscription this month. Can I get a refund?"

Expected Output:

EmailClassification(
    category="Billing",
    confidence=0.9,
    routed_response="Our billing department will review your account and respond within 2 business days."
)

If you are not familiar with these LangChain components, you may find the following references useful:

Initial code (Python 3.12 + LangChain 1.2.10)

"""
Smart Support Router - Solution Template

Your task: Implement the classify_and_route_email function below.
"""

from langchain_core.prompts import PromptTemplate
from langchain_core.output_parsers import PydanticOutputParser
from langchain_core.runnables import RunnableBranch, RunnableLambda

from pydantic import BaseModel, Field
from typing import Literal


class EmailClassification(BaseModel):
    # Structure for email classification output.
    category: Literal["Technical Support", "Billing", "General Feedback"] = Field(
        description="The category of the customer email"
    )
    confidence: float = Field(description="Confidence score between 0 and 1")
    routed_response: str = Field(
        description="The response from the specialized department"
    )


def classify_and_route_email(email_content: str, mock_llm_func) -> EmailClassification:
    pass

Except where otherwise noted, original content on this site is dedicated to the public domain under CC0 1.0.
Powered by Hexo & Theme mdsuper
沪ICP备2026040813号
Search