[Interview] SAP Student Training & Rotation Program(VT/STAR) Intern (Application Engineer) - 2028 Graduates (455960) Online Assessments
Categories Interview
Tags
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
nodeDocker image from the Docker Hub; - Node.js version can be configured with the
NODE_VERSIONbuild argument and defaults to13; - A lightweight Alpine version of the Node.js image is used, so when the version is set to
13the base image should benode: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
./projectdirectory to the current working directory; - Install all dependencies with the
npm installcommand; - 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.jsas 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:
The function has one input parameter
city, which is astringthat cannot be empty.- Throw the “not a string” message if the
cityparameter is not a string. - Throw the “string is empty” if
cityis an empty string.
- Throw the “not a string” message if the
The
BASE_URLvariable will be imported at the top of your inital file; you need to concatenate the query prefix&q=between theBASE_URLand thecityparameter (as the url) in order to fetch the data correctly like so:<base url><query prefix><city parameter>The function should call the mocked API and return an object that contains the weather data for the specified city. The
mainproperty 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 }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 witherror.data.messageparameter).
- This message is provided by the mocked-up API and can be sent back using
Assumptions
- The API function (
axios.get(url)orfetch(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)orfetch(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 theasyncprefix 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:
- Case 1: Input is not a string.
- Examples might be
null,undefined,{},[], ornumber(0,5,99,etc..). - Expected Throw:
not a string
- Examples might be
- Case 2: Input is an empty string.
- Expected Throw:
string is empty
- Expected Throw:
- 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)
- For Input:
- 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
@SerializedNameannotation. - 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
PostEntitylist (provided below). - You should convert date-time fields to
org.joda.time.DateTime. - For null or empty
updated_atproperties, usecreated_at, which is always available. - For null or empty
reactions_countproperties, 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:
- Transient client failures — network errors, rate limits, or timeouts when calling the model API.
- 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 byclient.complete()to signal a transient failure.ValidationError— may be raised byvalidate_fninstead of returningFalse. 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:
{"role": "assistant", "content": <the_failed_response>}— echoes the model’s failed output back{"role": "user", "content": <correction>}— a corrective prompt
The correction message depends on the failure type:
validate_fnreturnsFalse: use the constantCORRECTION_MESSAGEverbatimvalidate_fnraisesValidationError: 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:
- Classifies incoming emails into one of the three categories
- Routes each email to the appropriate specialized department handler
- 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 emailmock_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 confidencerouted_response: A string response from the specialized department handler
Implementation Requirements
Your solution must implement a two-step process:
- Classification - Analyze the email and determine its category
- 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:
- Use the
categoryfield to route to the appropriate department chain - Call the mock LLM again with a department-specific prompt
- Update the
routed_responsefield with the department’s response - Return the complete
EmailClassificationobject
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(typicallyPromptTemplate) to create structured prompts for classification and department responses PydanticOutputParser- Parse LLM output intoEmailClassificationobjectsRunnableBranch- Route to department-specific chains based on categoryRunnableLambda- Wrap department handler functions for use inRunnableBranch
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
Page views: · Visitors:
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号