Understanding Action Engines
This topic covers advanced concepts in the C3 Agentic AI Platform and may not be relevant to most users. This section assumes you are already familiar with C3 AI methods and how to implement the methods.
Action engines
In the C3 AI Type System, Action.Engines are responsible for compiling and invoking C3 AI methods. They also create the environments where these methods are compiled and executed. These environments must satisfy the Action.Requirements declared for the methods being executed, specifically when Action.Engine#canRun(Action.Requirement) returns true.
An Action.Engine includes all properties of an Action.Requirement, but it requires all optional properties of Action.Requirement. Both Action.Engine and Action.Requirement follow the same naming conventions.
Every Action.Engine has the following attributes:
- A
ImplLanguage.Runtimeruntime, such aspy,py-data, orpy-tf. - A
ImplLanguage.Executorexecutor, likejep,py4j, oripython. - A
ImplLanguage.RuntimeLocationlocation, such asclientorserver.
Resolving a runtime
An Action Engine executes actions in a resolved runtime. Each resolved runtime is created from its corresponding Action Engine. A resolved runtime includes:
- The libraries of the engine's
runtime, which also includes the libraries of the runtime's parents. - The libraries of the engine's
executor, with the runtime named{{language}}-{{executor}}, such aspy-jep. - The libraries of the engine's
location, configured per language, with the runtime named{{language}}-{{location}}, likejs-client. - Test libraries, which are only included if the app is in test mode, with the runtime named
{{language}}-testing, such aspy-testing.
An Action Engine may also include additional libraries in a resolved runtime to enhance system performance.
When a user resolves or upserts a runtime using ImplLanguage#resolveRuntime or ImplLanguage#upsertRuntime, at least one resolved runtime is created for each valid engine for the user-declared runtime. An engine is valid if its language supports the specified executor (see ImplLanguage#executors) and if the engine's executor supports the specified location (see ImplLanguage.Executor#locations). If any library conflicts arise during runtime resolution, the corresponding engine is considered invalid.
Staying in engine
When you call a method in C3 AI, it can either execute in the same action engine from which it was called or in a different action engine. If the method executes in the same context, it is referred to as staying in engine.
Staying in engine is desirable for both performance and functionality:
- Performance:
- If the method does not stay in engine, all arguments must be transferred from the calling context to the executing context, and the return value must be transferred back. This data transfer incurs overhead, sometimes significantly.
- If the method does not stay in engine, another engine must execute the action, creating or retrieving an engine, which also incurs overhead.
- Functionality:
- The C3 AI server cannot pass native values (e.g., an
sklearnpipeline instance) across engines. Therefore, methods that accept or return such native values must stay in engine to function properly.
- The C3 AI server cannot pass native values (e.g., an
A method stays in engine if the action engine of the current context can run the action requirements specified for the method. The section below explains what it means for an action engine to be capable of running an action requirement.
When can an engine run an action
All code in the C3 AI ecosystem runs in some Action.Engine. You can introspect the current Action.Engine using the following code snippet:
c3.Context.actionEngineTo determine whether your method executes locally (within the same engine) or remote to a different engine, the C3 AI server evaluates the following expression:
c3.<TypeName>.meta().method(<method name>).shouldStayInEngine(c3.Context.actionEngine)If this expression returns true, the method executes within the current action engine and is not be dispatched to a different engine (it will not remote).
Method#shouldStayInEngine returns true if any of the method's Action.Requirements (a method may have multiple, such as js | py) can be run by the provided Action.Engine. An Action.Engine can run an Action.Requirement (see Action.Engine#canRun) if:
- The
Action.Engine'sruntimecan run theAction.Requirement'sruntime(for example,py,py-data,py-data-science). - The
Action.Engine'sexecutorcan run theAction.Requirement'sexecutor, or if theAction.Requirementdoes not specify anexecutor(e.g.,jep,py4j,ipython). - The
Action.Engine'slocationcan run theAction.Requirement'slocation, or if theAction.Requirementdoes not specify alocation(e.g.,client,server).
Runtime compatibility
To determine if an Action.Engine's runtime can run an Action.Requirement's runtime, C3 AI uses ImplLanguage#canRun. This is determined based on runtime inheritance. Runtimes can specify libraries and parents. The runtime satisfies the libraries it specifies and the union of all libraries from its parents (and ancestors). Consequently, a descendant runtime can always run an ancestor runtime. runtimeA.canRun(runtimeB) returns true if any of the following are true:
runtimeAandruntimeBare the same.runtimeA.isDescendantOf(runtimeB).runtimeBdoes not directly declare any libraries, andruntimeAcan run all ofruntimeB's parents (for example, ifruntimeBdeclares no libraries and has one parentpy-foo, andruntimeAhas parentpy-foo, thenruntimeAcan runruntimeB).
Note: Since descendant runtimes can run ancestor runtimes, you may observe that an engine's runtime is not the same as the runtime claimed by the method the engine is executing. For example, your method claimed py-data may run in an engine with runtime py-data-science.
Executor and location compatibility
The checks for executor and location compatibility are simple equivalence checks. If an Action.Requirement does not specify an executor or location, it is considered compatible with all executors and locations. Otherwise, the engine's location or executor must exactly match the requirement's location or executor.
Example
Consider the following method:
foo: function(value: string): int js | py-data-clientAssume you are working within a Jupyter notebook that uses a kernel named py-data-science``. In this environment, the action engine is configured with the ipython executor and operates in the client location. As a result, your action engine is identified as py-data-science-client-ipython`.
When you invoke the foo method, the key question is whether it executes within the current action engine or if it is remoted to a different engine.
- Your current
Action.Engineispy-data-science-client-ipythonbecause you are in a Jupyter notebook with a kernel namedpy-data-science. This engine uses theipythonexecutor and operates in theclientlocation. - The method
foohas twoAction.Requirements:jsandpy-data-client.- Your engine does not satisfy the
jsrequirement since it’s a different language (py-data-sciencevs.js). - However, the
py-data-scienceruntime can run thepy-dataruntime becausepy-data-scienceis a descendant ofpy-data. - Your engine is also in the
clientlocation, which matches theclientlocation required byfoo. - Finally, since the
py-data-clientrequirement does not specify an executor, your engine'sipythonexecutor is acceptable.
- Your engine does not satisfy the
When you call foo from your engine, the method does not remote; it executes within the same engine (stay in the engine).
Caveats
If the current Action.Engine can run the method being called within it, it will. However, there are some exceptions:
- Methods can have annotations specifying target nodes, threads, or apps for execution. Such methods will remote to the correct node, thread, or app if necessary.
- Refer to the fields on
Ann.Callfor more information.
- Refer to the fields on
Engineinstances with a dedicated thread pool will have implicitAnn.Call#threadPoolannotations for their member methods, dispatching those methods to their dedicated thread pool.- If a method is called server-side and there is both a
javaimplementation and an implementation the currentAction.Enginecan run, thejavaimplementation will be called.- Users can modify this behavior with the
Ann.Call#stayInEngineannotation.
- Users can modify this behavior with the