DSL
Internally, Equistamp uses a DSL for sending tasks, parsing responses and grading replies. This DSL is basically a small subset of Clojure, without lambdas, macros, loops and most of the goodies one would expect. This lobotomization is intentional, to limit the harm of bad actors getting access to our systems.
Feel free to reach out to us at reports@equistamp.com if you need more functionality to be added. We won't add all requests, but missing core Clojure functions will probably be added quickly.
Tester
You can use our tester endpoint to check DSL code. That endpoint will return the result of running valid DSL code in order to make sure your code is correct. You can also just use any Clojure interpreter - as long as you only use symbols mentioned on this page, it should work.
Syntax
Data Types
Refer to the Clojure docs for a exhaustive description of the allowed types. Not all of the Clojure types will be supported, but the following is a short intro. One thing worth remembering, is that other than atoms, all data types are immutable, so calling a function that modifies its arguments will return a new object with the updated data, rather than modifying the passed in object.
- Symbols begin with a non-numeric character and can contain alphanumeric characters and
*,+,!,-,_,',?,<,>and= nil- is nothing/no value and is tests logical false. The empty list (()) also evaluates tonil- Integers work as expected, at least if not too large (under 2^32 should be fine)
- Floats also work as expected
- Strings are always between double quotes, not apostrophes. So
"asd"is a valid string, but'asd'is not - Keywords start with a colon and always evaluate to the same object. So
:keyis always exactly the same as:key- this does not hold true with strings, e.g."asd"is equal"asd", but they are different objects. Keywords also can work as thegetfunction, so(:bla {:bla 123})will return123, as it's getting the value of:blafrom the map{:bla 123} - The booleans are
trueandfalse - Lists are zero or more values in parentheses, so all of the following are valid lists:
()- this is functionally the same asnil(1 2 3)((3 2) (3 (4 5) (4 5)) (5 3))(+ 1 2 3 4)- this is equivalent to1 + 2 + 3 + 4
- Vectors are zero or more values in square brackets, e.g.
[1 2 3 4] - Maps are mapping of keys to values, so the same as Python dicts. Maps are defined by zero or more key value pairs enclosed in braces.
The keys and values can be any forms, and commas are treated as whitespace, so will be ignored.
Maps also act as the
getfunction, so({:bla 123} :bla)will return123, as this call amounts to "return the mapping of the:blakeyword". The following are valid mappings:{}{:bla 123}{"key 1" 312 "key 2" 423, "key 4" 342}{"key 1" 312, "key 2" 423, "key 4" 342}- this map is exactly the same as the previous one, as commas are ignored{:bla (1 (3 4))}
- Sets are zero or more forms enclosed in braces preceeded by
#. Sets can act as function that return whether it contains a given key, e.g.(#{1 2 3} 1)returnstrue, while(#{1 2 3} 42)will returnfalse. The following are valid sets:#{}#{1 2 3 4}#{1 1 1 1 1 1 1 1 1 1 1 1 1}- this is exactly the same as#{1}
-
Atoms are used for mutability. They are basically a container for an arbitrary value, which can be modified with the appropriate functions (mainly
deref,reset!andswap!). Atoms are created with the(atom <value>)function, and read with(deref <atom>), or@<atom>for convenience. The following are some examples:(atom 123)- creates an atom containing the literal123(deref my-atom)- returns the value ofmy-atom(deref (atom 312))- returns the value of the created atom, i.e.312@my-atom- returns the value ofmy-atom
Truthiness
In Clojure, all values are logically true or false. The only "false" values are false, nil and the empty list () (which is
the same as nil) - all other values are logically true. This can be a bit of a gotcha when checking for emptiness - in which case
use the seq function, which will return nil for empty collections, e.g. (seq "") => nil.
Evaluation
Clojure is a LISP, so every expression evaluates to something. Basic types (like numbers or strings) evaluate to themselves. Lists are evaluated as function calls, where the first element is the function, and all the other elements are the function's arguments. Here are some examples:
123->123"bla"->"bla"(+ 1 2 3 4)->10(+ (+ 1 3) (- 10 6) (* 2 2))->12(some-function "arg 1" "arg 2")-> the results of callingsome-functionwith"arg 1"and"arg 2", so the equivalent ofsomeFunction("arg 1", "arg 2")in more known languages like Python
Special forms
There are a few special forms in the language, everything else is handled by function. These special forms are:
if
Conditional execution. Defined as (if <test> <positive branch> <optional negative branch>). First <test> is evaluated. If the test
returns something that is non false (i.e. anything other than nil, () or false) then the result of evaluating <positive branch>
will be returned. Otherwise (i.e. if <test> returns (), nil or false) then the result of evaluating <negative branch>
will be returned. Only one branch will ever be evaluated. Some examples are:
(if true 1 2)->1(if false 1 2)->2(if true 1)->1(if false 1)->nil(if (+ 1 2) 3)->3(if (> 2 3) "larger" "smaller")->"smaller"(if (> val 3) "larger" "smaller")-> if the result of evaluatingvalis larger than3, this will return"larger", otherwise it'll return"smaller"
do
Execute a group of zero or more clauses, returning the result of evaluating the last clause. This is mainly useful for operations with side effects. Some examples are:
(do)->nil(do 1 2 3)->3(do (first-operation) (second-operation) (third-operation))-> the result of(third-operation)
let
let binds symbols to values in a "lexical scope". A lexical scope creates a new context for names, nested inside the
surrounding context. Names defined in a let take precedence over the names in the outer context.
The syntax is
(let [[symbol1 binding1]
[symbol2 binding2]
(...)]
(operation1)
(operation2)
(...))
Some examples are:
(let [[bla 12]] (+ 1 bla))->13(let [] (clause1) (clause2))-> the same as(do (clause1) (clause 2))(let [[one 1] [two 2] [three 3]] (+ one two three))->6
throw
Throws the result of evaluating expr. The available exceptions are EvaluationError, ValueError, RateLimitError, ServerError and
RetryError, all of which accept a single string. Some examples:
(throw (ValueError "help!"))(throw (if val (ServerError val) (ValueError "no value found"))
try
Catches exceptions. Syntax is (try expr* catch-clause* finally-clause?), where:
* catch-clause → (catch classname name expr*)
* finally-clause → (finally expr*)
The exprs are evaluated and, if no exceptions occur, the value of the last expression is returned. If an exception occurs and catch-clauses are provided, each is examined in turn and the first for which the thrown exception is an instance of the classname is considered a matching catch-clause. If there is a matching catch-clause, its exprs are evaluated in a context in which name is bound to the thrown exception, and the value of the last is the return value of the function. If there is no matching catch-clause, the exception propagates out of the function. Before returning, normally or abnormally, any finally-clause exprs will be evaluated for their side effects. Example:
(try
(+ 1 2)
(/ 123 0)
(catch ValueError e (str "caught: " e))
(finally (POST "http://my.endpoint")))
Core functions
All other functionality is provided via functions that are available in the default namespace. Most of these are taken directly from Clojure, so if in doubt please reference ClojureDocs.
Base functions
identity
Returns its argument, e.g. (identity 123) -> 123. This is very useful e.g. when filtering items, as you can use it
to remove any falsey items. Returns nil if no arguments provided. Examples:
(identity 123)->123(identity "123")->"123"(identity nil)->nil(identity)->nil
=
Checks equality. Receives 0 or more values, and will return true if they are all equal to each other. See the docs for specifics. Examples:
(=)->true(= false)->true(= false false)->true(= false false (= 1 2))->true(= 1 2 3 4)->false
or
Logical or. Receives 0 or more values, and will return the first value that evaluates truthy, going from left to right. Examples:
(or)->nil(or 1 2 3 4)->1(or false nil true false nil)->true
and
Logical and. Receives 0 or more values, and will return the first value going from left to right that evaluates falsey, or the last value if all are truthy. Examples:
(and)->nil(and 1 2 3 4)->4(and false nil true false nil)->nil
not
Logical negation. Will return true for false, nil or () and false for anything else. Examples:
(not false)->true(not nil)->true(not '())->true(not true)->false(not [])->false(not {})->false(not "bla bla bla")->false
Collection functions
seq
Returns a seq on the collection. If the collection is empty, returns nil. (seq nil) also returns nil. Examples are:
(seq nil)->nil(seq '())->nil(seq [])->nil(seq {})->nil(seq (set))->nil-
(seq "")->nil -
(seq "123")->(1 2 3) (seq (1 2 3))->(1 2 3)(seq [1 2 3])->[1 2 3](seq {:a 1, :b 2})->([:a 1] [:b 2])
count
Returns the number of items in the collection. Examples are:
(count nil)-> 0(count '())-> 0(count [])-> 0(count (set ))-> 0(count (1 2 3))-> 3(count "abcde")-> 5
set
Makes a set out of the provided collection. The collection must contain hashable items. Examples are:
(set nil)->#{}(set [1 2 3 4])->#{1 2 3 4}
Sets can also be used as functions, so this is a useful way of checking whether a list contains an item, e.g.:
((set [1 2 3]) 2)->true((set [1 2 3]) 42)->false
get
Returns the item at the given key in the provided collection. In the case of lists and vectors, key should be the index of
the desired item. The syntax is (get <collection> <key> <optional default value>). Both keywords and maps can be used instead
of the get function. Examples are:
(get [1 2 3] 0)->1(get {:bla 1} :bla)->1(get {:bla 1} :not-found)->nil(get {:bla 1} :not-found 432)->423({:bla 2} :bla)->2({:bla 2} :not-found)->nil({:bla 2} :not-found 43)->43(:bla {:bla 2})->2(:not-found {:bla 2})->nil(:not-found {:bla 2} "default")->"default"
get-in
Returns the value at the provided path. Syntax is (get-in <collection> <path> <optional default value>). Examples are:
(get-in {} [:a :b :c])->nil(get-in {:nested [1 2 3]} [:nested 2]")->3
assoc
Sets a value at the given key. Syntax is (assoc <collection> <key> <value>). In the case of lists and vectors, <key> should
be an appropriate index between 0 and the length of the collection. In the case of lists and vectors, all numerical keys will be
floored to integers, and anything else will raise an error. Examples are:
(assoc [1 2 3] 1 "bla")->[1 "bla" 3](assoc {:key1 1 :key2 2} :key1 32)->{:key1 32 :key2 2}(assoc {:key1 1 :key2 2} :key3 32)->{:key1 1 :key2 2 :key3 32}(assoc [1 2 3] -23 1)-> will raise an error(assoc [1 2 3] 100 1)-> will raise an error
assoc-in
Sets a value at the given path in a nested structure. Syntax is (assoc <collection> <path> <value>). If a key cannot be found in
a map, a new map will be added to contain it. The same limitations apply in the case of lists and vectors that apply for assoc.
Examples are:
(assoc-in [1 2 3] [1] "bla")->[1 "bla" 3](assoc-in {:outer {:inner {:a 2}}} [:outer :inner :b] "bla")->{:outer {:inner {:a 2 :b "bla"}}}(assoc-in {} [:outer :inner :b] "bla")->{:outer {:inner {:b "bla"}}}
every?
Goes over the provided <collection> and returns true if the <predicate> function evaluates as truthy for all values. Syntax is
(every? <predicate> <collection>). Some examples:
(every? #{1 2} [1 2 3 4])->false(every? (set list-to-check-against) list-to-check)-> generic way of checking whether two lists have the same elements
not-any?
Goes over the provided <collection> and returns true if the <predicate> function evaluates as truthy for none of the values. Syntax is
(not-any? <predicate> <collection>). Some examples:
(not-any? #{1 2} [1 2 3 4])->false(not-any? #{41} [1 2 3 4])->true
some
Goes over the provided <collection> and returns true if the <predicate> function evaluates as truthy for any value. Syntax is
(some <predicate> <collection>). Some examples:
(some #{1 2} [1 2 3 4])->true(some (set list-to-check-against) list-to-check)-> generic way of checking whether two lists have common elements
Functional functions
apply
Returns the result of calling <func> with the provided <args>, where <args> is a collection. Syntax is
(apply <func> a b c ... args). Examples:
(apply +)->0(apply + [1 2 3])->6(apply + 1 [1 2 3])->7(apply + 1 2 3 4 [1 2 3])->16(apply + 1 2)-> will raise an error, as the last argument must be a collection
partial
Returns a function that will append any arguments to the provided initial arguments and call <func>.
Syntax is
(partial <func> a b c & args). Examples:
((partial +))->0((partial + 1 2))->3((partial + 1 2) 3 4 5)->15
comp
Takes a set of functions and returns a fn that is the composition
of those fns. The returned fn takes a variable number of args,
applies the rightmost of fns to the args, the next
fn (right-to-left) to the result, etc. When no function is provided,
this will be equivalent to calling identity. Examples:
((comp))->nil((comp) 123)->123((comp str +) 1 2 3)->"6"((comp (partial * 2) +) 1 2 3)->12((comp (partial + 1) (partial + 1) (partial + 1) (partial + 1) +) 1 2 3)->10
map
Returns a lazily evaluated list of executing <func> on each item(s). Syntax is (map <func> <col1> <col2> ...). <func> must
accept as many arguments as there are collections provided, as it will be called with the subsequent values of each col, as long
as the cols still have values. This means that the length of the resulting list will be the same as the shortest input col.
Examples:
(map (partial + 1) [1 2 3])->[2 3 4](map + [1 2 3] [1 2 3 4 5 6])->[2 4 6]
reduce
f should be a function of 2 arguments. If val is not supplied,
returns the result of applying f to the first 2 items in coll, then
applying f to that result and the 3rd item, etc. If coll contains no
items, f must accept no arguments as well, and reduce returns the
result of calling f with no arguments. If coll has only 1 item, it
is returned and f is not called. If val is supplied, returns the
result of applying f to val and the first item in coll, then
applying f to that result and the 2nd item, etc. If coll contains no
items, returns val and f is not called. Syntax is (reduce f coll) or (reduce f val coll). Examples:
(reduce + [])->0(reduce + [12])->12(reduce + [1 2 3 4])->10(reduce + 10 [1 2 3 4])->20
filter
Lazily evaluates coll, returning all items for which (pred item) is True. Examples:
(filter (partial > 2) [1 2 3 4 5 6])->(3 4 5 6)(filter identity [1 0 true false nil () [2]])->(1 0 true [2])
remove
Lazily evaluates coll, returning all items for which (pred item) is False. Examples:
(remove (partial > 2) [1 2 3 4 5 6])->(1 2)(remove identity [1 0 true false nil () [2]])->(1 0 true nil () [2])
keep
Lazily evaluates coll, returning all items for which (pred item) is nil. Examples:
(keep (partial > 2) [1 2 3 4 5 6])->(1 2 3 4 5 6)(keep identity [1 0 true false nil () [2]])->(1 0 true false () [2])
take
Return a lazy sequence of the first n items of coll. Examples:
(take 0 [1 2 3 4 5 6])->()(take 3 [1 2 3 4 5 6])->(1 2 3)(take 103 [1 2 3 4 5 6])->(1 2 3 4 5 6)
take-while
Lazily evaluate coll, returning all items until (pred item) is falsey. Examples:
(take-while identity [1 2 3 nil 4 5 6])->(1 2 3)(take-while (partial > 5) [1 2 3 4 4 5 6])->(1 2 3 4 5)
drop
Return a lazy sequence after dropping the first n items of coll. Examples:
(drop 0 [1 2 3 4 5 6])->(1 2 3 4 5 6)(drop 3 [1 2 3 4 5 6])->(4 5 6)(drop 103 [1 2 3 4 5 6])->()
drop-while
Skip items of coll while (pred item) is truthy, after which return a lazy evaluated list of all the other items of coll. Examples:
(drop-while identity [1 2 3 nil 4 5 6])->(4 5 6)(drop-while (partial > 5) [1 2 3 4 4 5 6])->(6)
range
Returns a lazy seq of nums from start (inclusive) to end
(exclusive), by step, where start defaults to 0, step to 1, and end to
infinity. When step is equal to 0, returns an infinite sequence of
start. When start is equal to end, returns empty list. Syntax is one of:
(range)(range end)(range start end)(range start end step)
Examples:
(range)-> will return and infinite list of(0 1 2 3 4 ...)(range 10)->(0 1 2 3 4 5 6 7 8 9)(range 5 10)->(5 6 7 8 9)(range 2 10 2)->(2 4 6 8)(range 2 10 2)->()(range 10 0 -1)->(10 9 8 7 6 5 4 3 2 1)(range 2 0 0)-> will return and infinite list of(2 2 2 2 2 2 2 2 ...)
String functions
str
Makes a string by concatenating its arguments. Examples:
(str)->""(str "abc")->"abc"(str "abc" "e" "" "fg")->"abcefg"(str 1 " " 2 " " 3)->"1 2 3"(str 1 2 3)->"123"
trim
Removes any whitespace from both sides of a string. Examples:
(trim "bla")->"bla"(trim " bla")->"bla"(trim "bla \n")->"bla"(trim " bla ")->"bla"
upper-case
Returns the provided string as upper case. Examples:
(upper-case "abcd")->"ABCD"(upper-case "aBcD")->"ABCD"
lower-case
Returns the provided string as lower case. Examples:
(lower-case "ABcd")->"abcd"(lower-case "aBcD")->"abcd"
Atoms
Atoms are a way to add mutability. By default all data types are immutable - operations on them return new instances with the modified data. Atoms, on the other hand, can be directly changed using the appropriate functions
atom
Create a new atom. Syntax is (atom <value>) Example:
(atom 123)- create an atom with the value123(atom "some string")- create an atom with the value"some string"
deref
Gets the value of an atom. Example:
(deref (atom 123))- will return the value of the new atom, i.e.123
reset!
Set the value of an atom. Syntax is (reset! <atom> <new value>). Will return the new value. Examples:
(reset! (atom 123) 432)- will set the value of the atom to432, and also return 432(reset! some-atom (upper "bla"))- will set the value of the atom to"BLA"and also return"BLA"
swap!
Sets the value of an atom as the result of calling a function with the atom's current value. Syntax is
(swap! <atom> <function> & <args>), where <function> is the function to be called and <args> are
optional additional arguments to be provided to the function. The function must accept the atom's value
as its first argument. Returns the new value. Example:
(swap! (atom "abc") upper-case)- will change the value of the atom to be"ABC". Will return"ABC".(swap! (atom {:a 1}) assoc :a 2)- will change the value of the atom to the result of calling(assoc {:a 1} :a 2), so{:a 2}. Will return{:a 2}
Mathematical Comparisons
+
Returns the sum of all its arguments. Examples:
(+)-> 0(+ 1)-> 1(+ 1 2 3 4)-> 10
-
Returns the result of subtracting all its arguments. Examples:
(-)-> 0(- 1)-> -1(- 1 -2 3 -4)-> 4
*
Returns the result of multiplying all its arguments. Examples:
(*)-> 1(* 1)-> 1(* 1 2 3)-> 6(* 1 2 3 -0.5)-> -3
/
Returns the result of dividing all its arguments. If only one value is provided, it will
be used to divide 1. Examples:
(/ 10)->0.1(/ 20 5)->4(/ 30 3 5)->2
>
Returns true if each argument, going from left to right, is larger than the next one. Examples:
(> 4 3 2 1 0 -1 -2 -3)->true(> 4 3 1 2)->false
>=
Returns true if each argument, going from left to right, is larger than or equal to the next one. Examples:
(>= 4 3 2 2 2 2 2 2 1 0 -1 -2 -3)->true(>= 4 3 1 2)->false
<
Returns true if each argument, going from left to right, is smaller than the next one. Examples:
(< -2 -1 0 1 2 3 4)->true(> 1 3 2 4)->false
<=
Returns true if each argument, going from left to right, is smaller than or equal to the next one. Examples:
(<= -2 -1 0 1 1 1 1 1 2 3 4)->true(>= 1 3 2 4)->false
Network calls
These are generic network call functions.
GET
Send a GET request. The syntax is (GET <url> <config>), where <url> is
a string with a fully qualified URL, and <config> is a map with the following optional values:
:headers- any headers to be sent, as a map of {: }
Examples are:
(GET "http://my.service/endpoint")-> will just send a GET request(GET "http://my.service/endpoint" {:headers {"Api-Token" "DEADBEEF"}})-> will send a GET request with an"Api-Token"header
The response is returned as a map with :status, :headers, :json and :text keys, e.g.:
{
:status 200
:headers {"content-length" 33148}
:json {"response" "bla bla bla"}
:text "{\"response\": \"bla bla bla\"}"
}
POST
This is the basic function to use when calling external services. The syntax is (POST <url> <config>), where <url> is
a string with a fully qualified URL, and <config> is a map with the following optional values:
:headers- any headers to be sent, as a map of {: } :json- a structure that should be sent as JSON data:body- a string that will be sent as the body of the request. If:jsonis provided, this will be ignored
Examples are:
(POST "http://my.service/endpoint")-> will just send a POST request with no body(POST "http://my.service/endpoint" {:headers {"Api-Token" "DEADBEEF"}})-> will send a POST request with no body and an"Api-Token"header- A full blown request:
(POST "http://my.service/endpoint" { :headers { "Api-Key" "Some key" "X-Header" "bla bla" } :json { :type "task" :temperature 1 "max context window" 10000 :task { :text task-text }}})
The response is returned as a map with :status, :headers, :json and :text keys, e.g.:
{
:status 200
:headers {"content-length" 33148}
:json {"response" "bla bla bla"}
:text "{\"response\": \"bla bla bla\"}"
}
PUT
This is the basic function to use when calling external services. The syntax is (PUT <url> <config>), where <url> is
a string with a fully qualified URL, and <config> is a map with the following optional values:
:headers- any headers to be sent, as a map of {: } :json- a structure that should be sent as JSON data:body- a string that will be sent as the body of the request. If:jsonis provided, this will be ignored
Examples are:
(PUT "http://my.service/endpoint")-> will just send a PUT request with no body(PUT "http://my.service/endpoint" {:headers {"Api-Token" "DEADBEEF"}})-> will send a PUT request with no body and an"Api-Token"header- A full blown request:
(PUT "http://my.service/endpoint" { :headers { "Api-Key" "Some key" "X-Header" "bla bla" } :json { :type "task" :temperature 1 "max context window" 10000 :task { :text task-text }}})
The response is returned as a map with :status, :headers, :json and :text keys, e.g.:
{
:status 200
:headers {"content-length" 33148}
:json {"response" "bla bla bla"}
:text "{\"response\": \"bla bla bla\"}"
}
PATCH
This is the basic function to use when calling external services. The syntax is (PATCH <url> <config>), where <url> is
a string with a fully qualified URL, and <config> is a map with the following optional values:
:headers- any headers to be sent, as a map of {: } :json- a structure that should be sent as JSON data:body- a string that will be sent as the body of the request. If:jsonis provided, this will be ignored
Examples are:
(PATCH "http://my.service/endpoint")-> will just send a PATCH request with no body(PATCH "http://my.service/endpoint" {:headers {"Api-Token" "DEADBEEF"}})-> will send a PATCH request with no body and an"Api-Token"header- A full blown request:
(PATCH "http://my.service/endpoint" { :headers { "Api-Key" "Some key" "X-Header" "bla bla" } :json { :type "task" :temperature 1 "max context window" 10000 :task { :text task-text }}})
The response is returned as a map with :status, :headers, :json and :text keys, e.g.:
{
:status 200
:headers {"content-length" 33148}
:json {"response" "bla bla bla"}
:text "{\"response\": \"bla bla bla\"}"
}
OPTIONS
Send an OPTIONS request. The syntax is (OPTIONS <url> <config>), where <url> is
a string with a fully qualified URL, and <config> is a map with the following optional values:
:headers- any headers to be sent, as a map of {: }
Examples are:
(OPTIONS "http://my.service/endpoint")-> will just send a OPTIONS request(OPTIONS "http://my.service/endpoint" {:headers {"Api-Token" "DEADBEEF"}})-> will send a OPTIONS request with an"Api-Token"header
The response is returned as a map with :status, :headers, :json and :text keys, e.g.:
{
:status 200
:headers {"content-length" 33148}
:json {"response" "bla bla bla"}
:text "{\"response\": \"bla bla bla\"}"
}
DELETE
Send a DELETE request. The syntax is (DELETE <url> <config>), where <url> is
a string with a fully qualified URL, and <config> is a map with the following optional values:
:headers- any headers to be sent, as a map of {: }
Examples are:
(DELETE "http://my.service/endpoint")-> will just send a DELETE request(DELETE "http://my.service/endpoint" {:headers {"Api-Token" "DEADBEEF"}})-> will send a DELETE request with an"Api-Token"header
The response is returned as a map with :status, :headers, :json and :text keys, e.g.:
```
{
:status 200
:headers {"content-length" 33148}
:json {"response" "bla bla bla"}
:text "{\"response\": \"bla bla bla\"}"
}
External calls
For this DSL to make sense, it must be able to send tasks to external models. There is a generic POST function
that gives the most flexibility, but we also provide vendor specific functions to various popular AI model providers.
OpenAI
Calls the OpenAI endpoint. For this you must provide your API key and the desired model. Syntax is
(openai-call <your api key> <model name> <task text>). This call will return the same kind of response as the POST
function with OpenAIs response. An example call would be:
(openai-call "sk-your-secret-key" "gtp-4-turbo" task-text)
Anthropic
Calls the Anthropic endpoint. For this you must provide your API key and the desired model. Syntax is
(anthropic-call <your api key> <model name> <task text>). This call will return the same kind of response as the POST
function with Anthropic's response. An example call would be:
(anthropic-call "sk-your-secret-key" "claude" task-text)
AWS Bedrock
Calls a AWS Bedrock endpoint. For this you must provide an AWS access and secret key, along with the desired model. Syntax is
(bedrock-call <your access key> <your secret key> <model name> <task text>). This call will return the same kind of response as the POST
function with AWS's response. An example call would be:
(bedrock-call "your-access-key" "your-secret-key" "Jurassic" task-text)
Google VertexAI
Calls a Google VertexAI endpoint. For this you must provide an Google credentials JSON object encoded in base64, along with the
desired model. Syntax is (vertexai-call <your credentials> <model name> <task text>). This call will return the same kind of
response as the POST function with Google's response. An example call would be:
(vertexai-call "ZXhhbXBsZQ==" "chat-bison" task-text)
Mistral
Calls a Mistral endpoint. For this you must provide a Mistral API key, along with the
desired model. Syntax is (mistral-call <your api key> <model name> <task text>). This call will return the same kind of
response as the POST function with Mistral's response. An example call would be:
(mistral-call "your api key" "mistral-small" task-text)
together.ai
Calls a together.ai endpoint. For this you must provide a together.ai API key, along with the
desired model. Syntax is
(together-ai-call <your api key> <model name> <task text> <optional model type> <optional temperature> <optional max tokens>).
The default optional values are:
* model type - "completions". Possible values are "completions" or "chat/completions"
* temperature - 0.7
* max tokens - 600
This call will return the same kind of response as the POST function with together.ai's response. An example call would be:
(together-ai-call "your api key" "google/gemma-7b-it" task-text)
HuggingFace
HuggingFace models have separate calls to spin up an instance, call inference endpoints and then pause the models. You can see more here. To help with this, we provide the following functions:
Setup HuggingFace model
Starts a HuggingFace model. This does not mean that it's available right away, but this call will handle starting it in a
idenpotent way, so you can call this function multiple times. Syntax is (setup-hugging-face <api key> <model name> <model config>).
An example is:
(setup-hugging-face "your key" "model-123" {"name" "bla", "model" {"task" "text-generation"}})
Pause HuggingFace model
Pauses a HuggingFace model. You do not pay for paused models, but you can only have a certain (small) number of models per instance type
slot, so e.g. only 8 2xNvidia A100 instances. Once you hit that limit, you'll have to manually delete unwanted ones before you can
start another instance. Syntax is (pause-hugging-face <api key> <model config>).
An example is:
(pause-hugging-face "your key" {"name" "bla", "model" {"task" "text-generation"}})
Call HuggingFace model
Sends a request to a HuggingFace model's inference endpoint. Syntax is (hugging-face-call <api key> <model endpoint url> <model type> <prompt> <num choices>),
where:
<model endpoint url>is the endpoint to be called - this is generated for each instance and can be found in the HuggingFace console<model type>is the type of model to be evaluated. Should be one of"text-generation","text2text-generation","fill-mask","conversational"or"zero-shot-classification"<prompt>is the task to be evaluated<num choices>is an optional parameter that letszero-shot-classificationmodels know how many options they have to choose from
An example call is:
(hugging-face-call "an api key" "https://huggingface.inference.your.model/endpoint" "text-generation" task-text)
Helper functions
This is a grab bag of various addition functions that are useful, but not standard Clojure functions
to-json
Transforms a DSL data structure into something that can be serialised to JSON. Example calls:
(to-json 123)->123(to-json {:asd "ads"})->{"asd" "ads"}
arg-max
Returns the key with the largest value from the provided map. Example calls:
(arg-max {"a" 1 "b" 3 "c" 2})->"b"