Skip to main content

Backend Swapping

If you want to do mixture of agents or something along those lines, you can dynamically swap between backends.

Choices

Force the LLM to pick between multiple choices. It’s implemented by implemented by computing the token-length normalized log probabilities of all choices and selecting the one with the highest probability (full credit goes to the sglang folks for this).

Forking

Sometimes, you can reach a state in your program which is parallelizable.
In this case, the expanded tips for 1 and 2 don’t depend on each other, so we can generate both at the same time using the fork function. Then, we can join them back into the parent ProgramState and continue generating from there.

Streaming

Enochian of course also supports streaming, so you can use it for a chat application frontend.

Constrained decoding

For structured outputs, you can pass in either a regex, a JsonSchema, or a zod schema inside SamplingParams. Zod types are the preferred way since it allows for type safety in the .get.

Tool Calling

You can provide the LLM some tools that it can optionally use per generation. You should use the createTools util which will allow for greater type inference when retrieving the result.
It will return a ToolUseResponse typed object:
The LLM can either call one or multiple tools OR respond to the user but not both. The reason you want to use the createTools util is because the ToolUseResponse type cannot infer the literal value of toolUsed and the response type without it. With the createTools util, hovering over action in your IDE should show something like:
The LLM can either directly respond to the user or call the getWeather function and enochian is able to infer that. If you wanted to build a weather-getting agent, you could do a very simple β€œgame loop” like this:
That will repeatedly call the LLM, giving it the responses to its tool uses, until it decides it can finally directly respond to the user.

Max Prompt Tokens

A downside of gradually building up conversation history is that your context length can grow much larger than you’d like. In applications where time to first token is critical, like copilot, it’s critical to stay under a certain threshold of tokens you are submitting to the LLM at each time. The .gen function provides a transform callback that will take all the Messages added to the ProgramState previously, allow you to edit the array and return a new trimmed down array that .gen will use.
In this example we’re removing the oldest messages from the chat history until we’re under our maxPromptTokens threshold. Enochian provides a isUnderTokenThreshold(message: Message[], tokenThreshold: number, maxOutputTokens?: number) util function to make this check really easy. The tokenThreshold is for the number of tokens in the model’s context window you want to take up. You can optionally provide a maxOutputTokens to reserve some number of tokens in the context window for your output tokens (since the number of output tokens also counts towards your requests context length). If a maxOutputTokens is not passed in then zero tokens will be reserved for the output and the model will generate until it hits the max length or an end-of-sequence token. You can also provide metadata to .add and access that metadata inside the transform function to trim tokens out that way.
Enochian also provides transform presets for common patterns. You can do ranking by embedding similarity:
Or trim from middle to remove messages starting from the middle:
These presets can be combined however you like, since all they do is take in an array of Message and return the modified array. You can do trim from middle on previous chat history, and then do embedding similarity ranking on the current request:

Prefix Caching

Prefix caching is a common way to reduce computation needed for LLM requests and is also very relevant to the max prompt token transform function. If a common prefix of a very frequently used prompt is cached (think long system prompts), you can skip the entire computation of that prefix, effectively removing it from your max prompt tokens restriction. For that reason, if you specify messages with the metadata { probablyPrefixCached: true }, then they will NOT be provided to the transform function. Use this wisely!
You should probably always use fromPrefixCache: true for system prompts and any common prefixes in your prompts. Most providers automatically will do prefix caching for you as its also advantageous for them to reduce the amount of computation they have to do per request.
TODO: Put in relative priority example into api reference rawTransform