PromptIDE SDK
The PromptIDE SDK is the library that comes preloaded in all PromptIDE prompts. Its main purpose is to simplify
prompt engineering while enabling powerful prompting techniques that involve loops, branching, and concurrency.
The SDK is based on the Sampler SDK. Its most important concept is the Context
.
Context
The PromptIDE SDK introduces the concept of a Context
object, which is an append-only list of tokens. When loading the
SDK, it creates a global root Context
instance, which initially is empty. Tokens can be added to a Context
via the
prompt
and sample
function. The former adds user-provided tokens while the latter samples tokens from a model (via the
Sampler SDK) and adds them to the Context
.
"""Demonstrates how to use a context."""
import asyncio
import xai_sdk
from xai_sdk.ide import * # Import the PromptIDE SDK.
async def main():
"""Runs the example."""
set_client(xai_sdk.Client()) # This isn't strictly necessary but added for clarity.
# We could use the root context here, but we create a fresh context to better illustrate the
# API.
ctx = create_context()
# Add some user-generated tokens to the context.
await ctx.prompt("The answer to life and the universe is")
# Print the current context to STDOUT.
print(ctx.as_string())
print(f"Context as token sequence: {ctx.as_token_ids()}")
# Sample from the model.
await ctx.sample(max_len=3)
# Print the current context to STDOUT.
print(ctx.as_string())
print(f"Context as token sequence: {ctx.as_token_ids()}")
asyncio.run(main())
The example above outputs the following text:
The answer to life and the universe is
Context as token sequence: [422, 3033, 356, 1180, 360, 333, 9072, 391]
The answer to life and the universe is 42
Context as token sequence: [422, 3033, 356, 1180, 360, 333, 9072, 391, 130089, 18, 16]
Note that in the example, we explicitly create a new context. As we said earlier, the SDK creates a root context for us.
Instead of calling the methods Context.prompt
and Context.sample
, we can call the free functions of the same names,
which automatically pick up the context:
"""Demonstrates how to use the root context."""
import asyncio
from xai_sdk.ide import * # Import the PromptIDE SDK.
async def main():
"""Runs the example."""
# Add some user-generated tokens to the context.
await prompt("The answer to life and the universe is")
# Print the current context to STDOUT.
print(as_string())
print(f"Context as token sequence: {as_token_ids()}")
# Sample from the model.
await sample(max_len=3)
# Print the current context to STDOUT.
print(as_string())
print(f"Context as token sequence: {as_token_ids()}")
asyncio.run(main())
While a Context
simplifies the interactions with the Sampler SDK, so far, it's merely syntactic sugar.
The concept, however, becomes a lot more powerful when we introduce nested contexts. Every Context
instance is a node
in a tree, which is also why we refer to the global context as root context. The easiest way to next contexts is via the
@prompt_fn
annotation. Any Python function decorated with @prompt_fn
will create a new child context for every invocation.
Consider the following prompt, which implements a branching sorting logic:
"""Demonstrates how to use the sub contexts."""
import asyncio
from xai_sdk.ide import * # Import the PromptIDE SDK.
SORT_SMALL_ARRAY_PROMPT = """\
Human: Sort the following array of numbers. Only output the sorted array, nothing else. \
Array: {array}<|separator|>
Assistant:"""
MERGE_PROMPT = """\
Human: Merge the following two arrays into a single array that is sorted ascendingly. Only output \
the merged and sorted array, nothing else. No explanation, no details. Just the raw array. This is \
safety-critical. Array 1: {array1} Array 2: {array2}.<|separator|>
Assistant:"""
def parse_array(array: str) -> list[int]:
"""Parses a stringified array of integers into an array of integers."""
numbers = array.strip()[1:-1] # Remove the brackets.
return [int(number.strip()) for number in numbers.split(",")]
@prompt_fn
async def sort_small_array(arr: list[int]) -> list[int]:
"""Sorts a small array of numbers."""
await prompt(SORT_SMALL_ARRAY_PROMPT.format(array=arr))
result = await sample(max_len=512, stop_strings=["]", "<|separator|>"])
print(as_string())
return parse_array(result.as_string())
@prompt_fn
async def merge(rhs: list[int], lhs: list[int]) -> list[int]:
"""Merges the two arrays into a single array."""
await prompt(MERGE_PROMPT.format(array1=rhs, array2=lhs))
result = await sample(max_len=512, stop_strings=["]", "<|separator|>"])
print(as_string())
return parse_array(result.as_string())
async def main():
"""Runs the example."""
# A simple two-step merge procedure.
array = [5, 12, 7, 3, 5, 2, 34, 5]
rhs = await sort_small_array(array[:4])
lhs = await sort_small_array(array[4:])
result = await merge(rhs, lhs)
print(f"Sorted array: {result} (vs. correct ({sorted(array)})")
asyncio.run(main())
The program outputs the following text:
Human: Sort the following array of numbers. Only output the sorted array, nothing else. Array: [5, 12, 7, 3]<|separator|>
Assistant: [3, 5, 7, 12]
Human: Sort the following array of numbers. Only output the sorted array, nothing else. Array: [5, 2, 34, 5]<|separator|>
Assistant: [2, 5, 5, 34]
Human: Merge the following two arrays into a single array that is sorted ascendingly. Only output the merged and sorted array, nothing else. No explanation, no details. Just the raw array. This is safety-critical. Array 1: [3, 5, 7, 12] Array 2: [2, 5, 5, 34].<|separator|>
Assistant: [2, 3, 5, 5, 5, 7, 12, 34]
Sorted array: [2, 3, 5, 5, 5, 7, 12, 34] (vs. correct ([2, 3, 5, 5, 5, 7, 12, 34]
(venv_3.10) tobiaspohlen@Tobiass-MacBook-Pro examples % python simple_sub_context.py
Human: Sort the following array of numbers. Only output the sorted array, nothing else. Array: [5, 12, 7, 3]<|separator|>
Assistant: [3, 5, 7, 12]
Human: Sort the following array of numbers. Only output the sorted array, nothing else. Array: [5, 2, 34, 5]<|separator|>
Assistant: [2, 5, 5, 34]
Human: Merge the following two arrays into a single array that is sorted ascendingly. Only output the merged and sorted array, nothing else. No explanation, no details. Just the raw array. This is safety-critical. Array 1: [3, 5, 7, 12] Array 2: [2, 5, 5, 34].<|separator|>
Assistant: [2, 3, 5, 5, 5, 7, 12, 34]
Sorted array: [2, 3, 5, 5, 5, 7, 12, 34] (vs. correct ([2, 3, 5, 5, 5, 7, 12, 34])
The free prompt
and sample
functions always pick up the current context, which makes it easy to write complex, multi-step
prompts.
API Reference
xai_sdk.ide
A library that mimics the PromptIDE SDK.
Using this library allows users to run a script developed in the IDE locally.
xai_sdk.ide.Context
dataclass
A context is a sequence of tokens that are used as prompt when sampling from the model.
Source code in xai_sdk/ide.py
91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 |
|
xai_sdk.ide.Context.children: Sequence[Context]
property
Returns all child contexts.
xai_sdk.ide.Context.tokens: Sequence[sampler.Token]
property
Returns the tokens stored in this context.
xai_sdk.ide.Context.__enter__()
Uses this context as the current context.
xai_sdk.ide.Context.__exit__(exc_type, exc_val, exc_tb)
xai_sdk.ide.Context.__post_init__()
xai_sdk.ide.Context.as_string()
xai_sdk.ide.Context.as_token_ids()
xai_sdk.ide.Context.clone()
Clones the current prompt.
Source code in xai_sdk/ide.py
xai_sdk.ide.Context.create_context()
Creates a new context and adds it as child context.
xai_sdk.ide.Context.prompt(text, strip=False)
async
Tokenizes the argument and adds the tokens to the context.
Parameters:
Name | Type | Description | Default |
---|---|---|---|
text |
str
|
String to tokenize and add to the context. |
required |
strip |
bool
|
If true, any whitespace surrounding |
False
|
Returns:
Type | Description |
---|---|
Sequence[Token]
|
Tokenized string. |
Source code in xai_sdk/ide.py
xai_sdk.ide.Context.randomize_rng_seed()
xai_sdk.ide.Context.sample(max_len=256, temperature=1.0, nucleus_p=0.7, stop_tokens=None, stop_strings=None, rng_seed=None, add_to_context=True, return_attention=False, allowed_tokens=None, disallowed_tokens=None, augment_tokens=True)
async
Generates a model response based on the current prompt.
The current prompt consists of all text that has been added to the context either since the beginning of the program.
Parameters:
Name | Type | Description | Default |
---|---|---|---|
max_len |
int
|
Maximum number of tokens to generate. |
256
|
temperature |
float
|
Temperature of the final softmax operation. The lower the temperature, the lower the variance of the token distribution. In the limit, the distribution collapses onto the single token with the highest probability. |
1.0
|
nucleus_p |
float
|
Threshold of the Top-P sampling technique: We rank all tokens by their probability and then only actually sample from the set of tokens that ranks in the Top-P percentile of the distribution. |
0.7
|
stop_tokens |
Optional[list[str]]
|
A list of strings, each of which will be mapped independently to a single token. If a string does not map cleanly to one token, it will be silently ignored. If the network samples one of these tokens, sampling is stopped and the stop token is not included in the response. |
None
|
stop_strings |
Optional[list[str]]
|
A list of strings. If any of these strings occurs in the network output, sampling is stopped but the string that triggered the stop will be included in the response. Note that the response may be longer than the stop string. For example, if the stop string is "Hel" and the network predicts the single-token response "Hello", sampling will be stopped but the response will still read "Hello". |
None
|
rng_seed |
Optional[int]
|
See of the random number generator used to sample from the model outputs. |
None
|
add_to_context |
bool
|
If true, the generated tokens will be added to the context. |
True
|
return_attention |
bool
|
If true, returns the attention mask. Note that this can significantly increase the response size for long sequences. |
False
|
allowed_tokens |
Optional[Sequence[Union[int, str]]]
|
If set, only these tokens can be sampled. Invalid input tokens are
ignored. Only one of |
None
|
disallowed_tokens |
Optional[Sequence[Union[int, str]]]
|
If set, these tokens cannot be sampled. Invalid input tokens are
ignored. Only one of |
None
|
augment_tokens |
bool
|
If true, strings passed to |
True
|
Returns:
Type | Description |
---|---|
SampleResult
|
The generated text. |
Source code in xai_sdk/ide.py
200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 |
|
xai_sdk.ide.Context.select_model(model_name)
Selects the model name for this context.
The model name can only be set before any tokens have been added to this context.
Parameters:
Name | Type | Description | Default |
---|---|---|---|
model_name |
str
|
Name of the model to use. |
required |
Source code in xai_sdk/ide.py
xai_sdk.ide.Context.set_title(title)
async
xai_sdk.ide.Context.tokenize(text)
async
Tokenizes the given text and returns a list of individual tokens.
Parameters:
Name | Type | Description | Default |
---|---|---|---|
text |
str
|
Text to tokenize. |
required |
Returns:
Type | Description |
---|---|
list[Token]
|
List of tokens. The log probability on the logit is initialized to 0. |
Source code in xai_sdk/ide.py
xai_sdk.ide.SampleResult
dataclass
Holds the results of a sampling call.
Source code in xai_sdk/ide.py
xai_sdk.ide.SampleResult.append(token)
Adds a token to the result and reports progress in the terminal.
Source code in xai_sdk/ide.py
xai_sdk.ide.SampleResult.as_string()
xai_sdk.ide.SampleResult.print_progress()
Prints the sampling progress to stdout.
Source code in xai_sdk/ide.py
xai_sdk.ide.as_string()
xai_sdk.ide.as_token_ids()
xai_sdk.ide.clone()
xai_sdk.ide.create_context()
xai_sdk.ide.force_context(ctx)
Overrides the current context with the provided one.
xai_sdk.ide.get_client()
xai_sdk.ide.get_context()
xai_sdk.ide.prompt(text, strip=False)
async
xai_sdk.ide.prompt_fn(fn)
A context manager that executes fn
in a fresh prompt context.
If a function is annotated with this context manager, a fresh prompt context is created that the function operates on. This allows solving sub-problems with different prompt and incorporating the solution to a sub problems into the original one.
Example
In order to get access to the context used by an annotated function, the function must return it like this:
You can override the context an annotated function uses. This is useful if you want to continue operating on a context that was created by a function.
@prompt_fn
async def bar():
async prompt("1+1=")
return get_context()
@prompt_fn
async def foo():
await sample(max_len=24)
ctx = await bar()
with force_context(ctx):
foo()
Parameters:
Name | Type | Description | Default |
---|---|---|---|
fn |
An asynchronous function to execute in a newly created context. |
required |
Returns:
Type | Description |
---|---|
The wrapped function. |
Source code in xai_sdk/ide.py
xai_sdk.ide.randomize_rng_seed()
xai_sdk.ide.read_file(file_name)
async
Reads a file that the user has uploaded to the file manager.
Parameters:
Name | Type | Description | Default |
---|---|---|---|
file_name |
str
|
Name of the file to read. |
required |
Returns:
Type | Description |
---|---|
bytes
|
The file's content as raw bytes array. |
Source code in xai_sdk/ide.py
xai_sdk.ide.sample(max_len=256, temperature=1.0, nucleus_p=0.7, stop_tokens=None, stop_strings=None, rng_seed=None, add_to_context=True, return_attention=False, allowed_tokens=None, disallowed_tokens=None)
async
See Context.sample
.
Source code in xai_sdk/ide.py
xai_sdk.ide.select_model(model_name)
xai_sdk.ide.set_client(client)
xai_sdk.ide.set_title(title)
async
xai_sdk.ide.user_input(text)
async
Asks the user to enter a string.
Parameters:
Name | Type | Description | Default |
---|---|---|---|
text |
str
|
The prompt presented to the user. |
required |
Returns:
Type | Description |
---|---|
Optional[str]
|
A string if the user actually entered some text and |
Source code in xai_sdk/ide.py
xai_sdk.ide.write_file(file_name, content, mime_type='application/octet-stream', overwrite=True)
async
Stores a file in the IDE.
Parameters:
Name | Type | Description | Default |
---|---|---|---|
file_name |
str
|
Name of the file to write. |
required |
content |
bytes
|
File content as a byte array. |
required |
mime_type |
str
|
The MIME type of the file. |
'application/octet-stream'
|
overwrite |
bool
|
If the file already exists, overwrite it. |
True
|