<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Stylianos Kamnakis</title>
    <description>The latest articles on DEV Community by Stylianos Kamnakis (@kamnakis).</description>
    <link>https://dev.to/kamnakis</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F3327012%2F8c3d68a2-0ea8-43d0-866a-797b74588f6f.png</url>
      <title>DEV Community: Stylianos Kamnakis</title>
      <link>https://dev.to/kamnakis</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/kamnakis"/>
    <language>en</language>
    <item>
      <title>Prototype: Voice-Controlling Web Apps with LLMs and Actions</title>
      <dc:creator>Stylianos Kamnakis</dc:creator>
      <pubDate>Sat, 05 Jul 2025 23:37:48 +0000</pubDate>
      <link>https://dev.to/kamnakis/talking-to-uis-a-humble-exploration-4j8o</link>
      <guid>https://dev.to/kamnakis/talking-to-uis-a-humble-exploration-4j8o</guid>
      <description>&lt;p&gt;I've been tinkering with an idea that I can't seem to shake: what if we could interact with web applications just by talking to them? Not in a gimmicky way, but in a way that genuinely makes them faster and more accessible to use. This isn't a new idea, of course. The command line is the original conversational interface, and we've seen assistants on our phones and in our homes for years. But I've been feeling like there's a gap when it comes to web applications themselves.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Little Problem That's Actually a Big Problem
&lt;/h2&gt;

&lt;p&gt;Modern web apps are powerful. They can be feature-rich, complex, and, let's be honest, sometimes a little overwhelming. We've all been there—clicking through nested menus, hunting for that one specific function, or trying to remember the precise sequence of operations to get something done.&lt;/p&gt;

&lt;p&gt;This complexity can be a barrier. For power users, it might be a minor annoyance, a few wasted seconds. But for new users, it can be a steep learning curve. And for users with accessibility needs, a complex UI can be a significant hurdle. What if we could flatten that curve? What if, instead of hunting and clicking, you could simply state your intent?&lt;/p&gt;

&lt;p&gt;That's the vision that got me started on this little project. The gut feeling is that by enabling users to express their goals in natural language—"add a new task called 'buy milk' and mark it as important," or "show me all the high-priority items in the 'work' project"—we could create a more fluid and intuitive user experience.&lt;/p&gt;

&lt;h2&gt;
  
  
  The First Step: A Proof of Concept
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fb4l6s4l41zoesclgl5wv.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fb4l6s4l41zoesclgl5wv.gif" alt="NLI in Todo App" width="800" height="520"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;To test this idea, I put together a small framework, which I've been calling &lt;strong&gt;Kinesis&lt;/strong&gt;. The core concept is to bridge the gap between a user's natural language input and the application's executable functions. Based on the code I've been working on, the flow looks something like this:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Action Registration&lt;/strong&gt;: The application developer defines a set of "actions" that a user can perform. These are just plain JavaScript functions with a name, a description, and a schema for the parameters they expect. In a React context, this could be done with a simple hook like &lt;code&gt;useKinesisAction&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Manifest Generation&lt;/strong&gt;: The framework collects all these registered actions into a "manifest." This manifest is essentially a menu of capabilities that the application has. It describes what the app can do in a structured way.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The AI Agent&lt;/strong&gt;: When a user types a command, it's sent to a backend "agent." This agent, powered by a large language model (LLM) like GPT-4 or a local model via Ollama, is given the user's message, the manifest of available actions, and the current state of the application.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Intent to Action&lt;/strong&gt;: The LLM's job is to act as a translator. It looks at the user's request and the list of available tools (the actions in the manifest) and determines which function, or sequence of functions, to call. It then returns the name of the action and the parameters to execute it with.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Execution&lt;/strong&gt;: The frontend receives the action(s) from the agent and runs them, updating the UI accordingly.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;I've built out a small to-do list application to try this out. It has actions like &lt;code&gt;add-todo&lt;/code&gt;, &lt;code&gt;delete-todo&lt;/code&gt;, and even &lt;code&gt;reorder-todos&lt;/code&gt;. Being able to type "add a task to buy groceries" and see it appear in the list feels... promising.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Bumps in the Road: Implementation Challenges
&lt;/h2&gt;

&lt;p&gt;Getting this to work, even as a prototype, brought a few interesting challenges to the surface.&lt;/p&gt;

&lt;p&gt;First was the question of &lt;strong&gt;context&lt;/strong&gt;. For the AI to make good decisions, it needs to understand not just the user's command but also the current state of the application. I decided to serialize the application's state and send it along with the prompt. This gives the model a snapshot of what's happening on the screen, allowing for more intelligent suggestions. For example, if you say "delete the first item," the AI needs to know what the "first item" actually is.&lt;/p&gt;

&lt;p&gt;Another challenge was &lt;strong&gt;action definition and discovery&lt;/strong&gt;. How do you make it easy for developers to expose their app's functionality to the AI? The approach I've taken is a registration pattern (&lt;code&gt;registerAction&lt;/code&gt;). In a React app, custom hooks can wrap this, making it almost declarative. A component can simply say, "here's an action I support," and the framework handles the rest.&lt;/p&gt;

&lt;p&gt;Finally, there's the &lt;strong&gt;LLM adapter&lt;/strong&gt; layer. Not everyone wants to use the same language model. I created a simple &lt;code&gt;LLMAdapter&lt;/code&gt; interface that allows plugging in different AI providers. I've implemented adapters for both OpenAI and Ollama so far. This allows for flexibility—a developer could use a powerful cloud model for production but a local, free model for development.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Leap to a Library: The Next Set of Challenges
&lt;/h2&gt;

&lt;p&gt;Turning this from a project-specific implementation into a generic, reusable library is a whole other mountain to climb. The challenges here are less about getting it to &lt;em&gt;work&lt;/em&gt; and more about getting it to be &lt;em&gt;adoptable&lt;/em&gt;.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Generality vs. Specificity&lt;/strong&gt;: How do you create a library that is general enough to work for any application but specific enough to be genuinely useful? The current implementation is tied to my to-do list's state management. A truly universal library would need to be agnostic about how the host application manages its state.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Developer Experience&lt;/strong&gt;: For a library like this to be successful, it has to be incredibly easy for developers to integrate. This means clear documentation, a simple API, and as little boilerplate as possible. The &lt;code&gt;useKinesisAction&lt;/code&gt; hook is a step in this direction, but there's more to be done.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Security&lt;/strong&gt;: As soon as you're executing functions based on AI-generated output, you have to think about security. What if the model hallucinates a function call that could have unintended consequences? We need robust validation and maybe even a permission model for actions. Furthermore, implementing a &lt;strong&gt;versioning system&lt;/strong&gt; for actions could be crucial. This would provide a clear audit trail and rollback capability for the actions the take place.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Performance&lt;/strong&gt;: Making a round trip to a server for every command might not always be ideal. For certain actions, we might want to explore running smaller, on-device models for faster feedback, reserving the larger models for more complex queries.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Tutorial: Bringing it to Life with Code
&lt;/h2&gt;

&lt;p&gt;Let's look at some simplified code snippets to illustrate how this natural language UI framework can be integrated into a React application. The core idea revolves around defining actions and then letting the AI agent pick and execute them based on user input.&lt;/p&gt;

&lt;h3&gt;
  
  
  1. Defining and Registering an Action
&lt;/h3&gt;

&lt;p&gt;The &lt;code&gt;useKinesisAction&lt;/code&gt; hook is the primary way to expose an application's functionality to the AI. It takes an &lt;code&gt;ActionDef&lt;/code&gt; object, which includes the action's &lt;code&gt;name&lt;/code&gt;, a &lt;code&gt;description&lt;/code&gt; for the AI, a &lt;code&gt;params&lt;/code&gt; schema, and the &lt;code&gt;handler&lt;/code&gt; function that performs the actual work.&lt;/p&gt;

&lt;p&gt;Here's how you might define a simple "add-todo" action within a React component:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;useKinesisAction&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@kinesis-framework/react&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;// Assuming this is an installed external package&lt;/span&gt;

&lt;span class="c1"&gt;// ... (other imports and component setup)&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;TodosPage&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="c1"&gt;// Assuming you have a state management function like this&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;addTodo&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;useTodosStore&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

  &lt;span class="nf"&gt;useKinesisAction&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
    &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;add-todo&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;description&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Add a new todo item to the list&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;params&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;text&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;string&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="c1"&gt;// The text content of the todo&lt;/span&gt;
      &lt;span class="na"&gt;done&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;boolean&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="c1"&gt;// Whether the todo should be marked as done initially&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="na"&gt;handler&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;text&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;done&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="c1"&gt;// This is the actual function that modifies your application state&lt;/span&gt;
      &lt;span class="nf"&gt;addTodo&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;text&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;done&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`Added todo: "&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;text&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;" (Done: &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;done&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;)`&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;

  &lt;span class="c1"&gt;// ... (rest of your component's JSX)&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In this example:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;name: "add-todo"&lt;/code&gt;: A unique identifier for this action.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;description&lt;/code&gt;: This is crucial. It tells the AI &lt;em&gt;what&lt;/em&gt; this action does in plain language. The more descriptive, the better the AI's understanding.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;params&lt;/code&gt;: Defines the expected input for the &lt;code&gt;handler&lt;/code&gt;. Here, &lt;code&gt;text&lt;/code&gt; is a string and &lt;code&gt;done&lt;/code&gt; is a boolean. The framework uses this schema to guide the AI in extracting parameters from the user's natural language.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;handler&lt;/code&gt;: This is your application's logic. When the AI decides to call &lt;code&gt;add-todo&lt;/code&gt;, this function will be executed with the parameters extracted from the user's prompt.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;A possible upside of using &lt;code&gt;useKinesisAction&lt;/code&gt; within components is the flexibility to define actions that are relevant only to the current view or page. Just like event listeners are scoped to the elements they are attached to, actions registered with &lt;code&gt;useKinesisAction&lt;/code&gt; are available only for the lifetime of the component they are defined in. This means actions from a previous page (e.g., if you navigate away) will automatically be unregistered and not be callable by the AI, preventing unintended side effects.&lt;/p&gt;

&lt;p&gt;If you desire global actions that are always available regardless of the current page or view, you would define them in a higher-level layout component (e.g. your main App component or a root layout) that persists across navigation.&lt;/p&gt;

&lt;h3&gt;
  
  
  2. Triggering the AI Intent
&lt;/h3&gt;

&lt;p&gt;Once actions are registered, you need a way to send the user's natural language input to the AI agent and then execute the returned actions. The &lt;code&gt;askAndRunAIIntent&lt;/code&gt; function simplifies this process.&lt;/p&gt;

&lt;p&gt;Typically, you'd connect this to an input field or a voice command interface.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt;&lt;span class="c1"&gt;// In your main application component or a command bar component&lt;/span&gt;

&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;askAndRunAIIntent&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@kinesis-framework/run&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;// Assuming this is an installed external package&lt;/span&gt;

&lt;span class="c1"&gt;// ... (other imports and component setup)&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;App&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;inputValue&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;setInputValue&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;useState&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;''&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="c1"&gt;// Assuming you have some global application state you want to send to the AI&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;appState&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;useTodosStore&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getState&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt; &lt;span class="c1"&gt;// Example: get current todos state&lt;/span&gt;

  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;handleCommandSubmit&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;preventDefault&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;inputValue&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;trim&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;''&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="c1"&gt;// Send the user's message and the current app state to the AI agent&lt;/span&gt;
      &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;success&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;askAndRunAIIntent&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
        &lt;span class="na"&gt;message&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;inputValue&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;state&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;appState&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="c1"&gt;// Provide the current application state for context&lt;/span&gt;
      &lt;span class="p"&gt;});&lt;/span&gt;

      &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;success&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;AI intent processed successfully!&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="nf"&gt;setInputValue&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;''&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// Clear input on success&lt;/span&gt;
      &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;AI could not process the intent or found no relevant action.&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="c1"&gt;// Optionally, shake a command bar or show an error message&lt;/span&gt;
      &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;catch &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Error processing AI intent:&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="c1"&gt;// Handle error, e.g., show a user-friendly message&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;};&lt;/span&gt;

  &lt;span class="k"&gt;return &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;form&lt;/span&gt; &lt;span class="na"&gt;onSubmit&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;handleCommandSubmit&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="cm"&gt;/* Your main application UI */&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
      &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;input&lt;/span&gt;
        &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"text"&lt;/span&gt;
        &lt;span class="na"&gt;value&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;inputValue&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
        &lt;span class="na"&gt;onChange&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;setInputValue&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;target&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
        &lt;span class="na"&gt;placeholder&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"Type a command, e.g., 'add buy milk'"&lt;/span&gt;
      &lt;span class="p"&gt;/&amp;gt;&lt;/span&gt;
      &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;button&lt;/span&gt; &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"submit"&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;Go&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;button&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;form&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In this snippet:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The &lt;code&gt;inputValue&lt;/code&gt; holds the user's natural language command.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;appState&lt;/code&gt; is passed to the AI agent, providing crucial context for the LLM to make informed decisions (e.g., if the user says "delete the last item," the AI needs to know what the "last item" is).&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;askAndRunAIIntent&lt;/code&gt; handles the network request to your backend AI endpoint (&lt;code&gt;/api/ai/intent&lt;/code&gt;) and then executes the actions returned by the AI.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This simplified view demonstrates the core interaction loop: define capabilities, send user input with context, and execute AI-determined actions. The beauty lies in abstracting the complex LLM interaction behind simple function calls, allowing developers to focus on defining their application's capabilities.&lt;/p&gt;

&lt;h3&gt;
  
  
  3. Adding More Actions: A Seamless Process
&lt;/h3&gt;

&lt;p&gt;One of the design goals for this framework was to make extending the natural language capabilities as straightforward as possible. Once the initial setup is in place, adding new actions is a highly declarative process.&lt;/p&gt;

&lt;p&gt;Consider if you wanted to add an action to "mark a todo as done." You would simply add another &lt;code&gt;useKinesisAction&lt;/code&gt; call within your component:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Inside your React component (e.g., page.tsx)&lt;/span&gt;

&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;useKinesisAction&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@kinesis-framework/react&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="c1"&gt;// ... (other imports)&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;Home&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;addTodo&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;doneTodo&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;deleteTodo&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;useTodosStore&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt; &lt;span class="c1"&gt;// Assume doneTodo is available&lt;/span&gt;

  &lt;span class="c1"&gt;// Existing 'add-todo' action&lt;/span&gt;
  &lt;span class="nf"&gt;useKinesisAction&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
    &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;add-todo&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;description&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Add a new todo item to the list&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;params&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;text&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;string&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;done&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;boolean&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="na"&gt;handler&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;text&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;done&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nf"&gt;addTodo&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;text&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;done&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`Added todo: "&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;text&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;" (Done: &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;done&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;)`&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;

  &lt;span class="c1"&gt;// New 'mark-todo-done' action&lt;/span&gt;
  &lt;span class="nf"&gt;useKinesisAction&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
    &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;mark-todo-done&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;description&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Marks a specific todo item as completed&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;params&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;string&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="c1"&gt;// The ID of the todo item to mark as done&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="na"&gt;handler&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;id&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nf"&gt;doneTodo&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// Your application's function to mark a todo as done&lt;/span&gt;
      &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`Marked todo with ID "&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;" as done.`&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;

  &lt;span class="c1"&gt;// Another example: 'delete-todo'&lt;/span&gt;
  &lt;span class="nf"&gt;useKinesisAction&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
    &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;delete-todo&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;description&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Deletes a specific todo item from the list&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;params&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;string&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="c1"&gt;// The ID of the todo item to delete&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="na"&gt;handler&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;id&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nf"&gt;deleteTodo&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// Your application's function to delete a todo&lt;/span&gt;
      &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`Deleted todo with ID "&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;".`&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;

  &lt;span class="c1"&gt;// ... (rest of your component's JSX)&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;As you can see, adding new functionality for natural language interaction is as simple as:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Defining a unique &lt;code&gt;name&lt;/code&gt; for the action.&lt;/li&gt;
&lt;li&gt;Providing a clear, descriptive &lt;code&gt;description&lt;/code&gt; for the AI.&lt;/li&gt;
&lt;li&gt;Specifying the &lt;code&gt;params&lt;/code&gt; schema to guide the AI in extracting necessary information.&lt;/li&gt;
&lt;li&gt;Implementing the &lt;code&gt;handler&lt;/code&gt; function that directly calls your existing application logic.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The framework automatically handles the registration of this new action with the &lt;strong&gt;manifest&lt;/strong&gt;, making it available to the AI agent without any further manual configuration or retraining of the LLM. This design aims to minimize friction for developers, allowing them to rapidly expand the natural language capabilities of their applications.&lt;/p&gt;

&lt;h2&gt;
  
  
  Where to From Here?
&lt;/h2&gt;

&lt;p&gt;I'm not claiming to have solved all these problems. This is just the start of an exploration. But the initial feeling is one of cautious optimism. There's something powerful about the idea of making our applications more conversational, more human-centric.&lt;/p&gt;

&lt;p&gt;My next steps will likely involve trying to abstract the core logic into a standalone package and testing it with a different, more complex application. The goal isn't to build a massive, all-encompassing framework, but rather to continue exploring this space and see if this gut feeling leads to something genuinely useful. I'd be curious to hear if others have been thinking along the same lines.&lt;/p&gt;

</description>
      <category>nli</category>
      <category>react</category>
      <category>webdev</category>
      <category>ai</category>
    </item>
  </channel>
</rss>
