Eris: integra el poder de la inteligencia artificial en tu IDE

0
657
Eris
Eris

Eris es un plugin open source para IntelliJ IDEA, el cual nos permite integrar nuestro IDE con la API Codex, de OpenAI.

Su concepción ha sido realizada en Autentia respondiendo a la necesidad de tener una herramienta confiable, la cual no inspeccione nuestros proyectos, y tener la total certeza de que los datos enviados a terceros son los que queremos enviar. Debido a esa transparencia, el proyecto es open source, y en este tutorial explicamos cómo ha sido creado.

Puedes acceder al plugin a través de su repositorio de GitHub.

0. Índice de contenidos


  1. Requisitos
  2. Entorno de trabajo
  3. Presentación del proyecto
  4. Servicios
  5. Menú de configuración
  6. Conclusiones

1. Requisitos


En un artículo previo mostrábamos cómo crear un plugin desde cero para el IDE IntelliJ IDEA: por qué embarcarnos en esa empresa, qué necesidades podía cubrir y, lo más importante, la creación y configuración del plugin, componentes, extensiones y acciones, y la compilación y puesta en producción.

En este nuevo tutorial vamos a continuar donde lo dejamos e introduciremos nuevos conceptos. Si aún no lo has leído, te invitamos a hacerlo ya que muchos conceptos los vamos a dar como asentados y vamos a presentar algunos nuevos.

2. Entorno de trabajo


Durante la creación de este tutorial se ha usado el siguiente entorno:

  • Hardware: Portátil MacBook Pro (16 pulgadas, 2021, Apple M1 Pro, 32 GB RAM)
  • Sistema Operativo: macOS Monterey 12.5.1
  • IntelliJ IDEA Ultimate 2023.1.2

3. Presentación del proyecto


Tal y como hemos comentado en la entradilla, Eris es un plugin que nos permite tener integrado en nuestro IDE la API Codex de OpenAI. Codex es un sistema que traduce el lenguaje natural a código, usando el modelo de ChatGPT. Para este fin, Eris está dividido en tres partes:

1. Extensión situada en el menú derecho del IDE.

Extensión
Extensión

Como podemos observar, la extensión tiene tres apartados diferenciados:

  • En primer lugar, en la parte superior, podemos observar el Contexto del plugin. Este apartado de texto sirve para poder introducir contexto a nuestro PROMPT. Se puede hacer de manera manual, escribiendo algunas líneas, o se puede añadir «cachos» de nuestro código. Para ello, seleccionamos en el Editor las líneas a añadir, pulsamos botón derecho de ratón, y nos aparece una acción llamada «Add to Eris», la cual copiará el código seleccionado en nuestro Contexto.
  • En la parte central, tenemos el PROMPT del plugin. Dicho campo es usado para lanzar la consulta a la IA. Si hubiera información en el campo de Contexto, éste sería añadido como contexto al PROMPT.
  • Por último, en la parte inferior, nos encontramos con la respuesta de la IA devuelta por la API.

2. Acción para mover código al contexto de interacción con la IA.

Como ya mencionamos, el plugin nos da la opción de añadir código a nuestro contexto. para ello, basta con seleccionar un trozo de código en en Editor, y la opción «Add to Eris» aparece en el menú de acciones:

Si clicamos, comprobamos que el texto es pegado en el Contexto. El contenido de este apartado (Contexto) irá enviado como parte del PROMPT, por lo tanto tenemos la habilidad de establecer qué información queremos que salga desde nuestro IDE a una plataforma de terceros.

Añadir a contexto
Añadir a contexto

3. Configuración de la API Key de OpenAI en el Settings del IDE.

Tal y como vimos en el anterior artículo, para interactuar con la API de OpenAI necesitamos crear una cuenta y generar una API Key. Para que el plugin pueda realizar su cometido, necesitamos introducir esta Key generada. Para ello, debemos introducir esta Key en el apartado «Eris Settings» que se encuentra en Settings/Tools.

Settings
Settings

El plugin se encuentra alojado en su repositorio de GitHub, el cual puede ser clonado para su instalación o para la revisión de lo que a continuación se expone en el tutorial.

4. Servicios


Para establecer una arquitectura un poco más limpia dentro del proyecto, hemos usado lo que el IntelliJ Platform nombra como Services.

Estos Services son componentes que se cargan bajo demanda. Según la documentación, el IntelliJ Platform asegura que sólo una instancia del servicio es cargada. Es lo más cercano a poder tener una inyección de dependencias dentro de IntelliJ Platform.

Hemos creado un Servicio que aloja la comunicación saliente con la API de OpenAI. Para definir este servicio, primero tenemos que crear la configuración propia en nuestro archivo plugin.xml. Dentro del tag extensions añadimos lo siguiente:

<applicationService
                serviceInterface="com.autentia.eris.service.ApiService"
                serviceImplementation="com.autentia.eris.service.OpenAIApiServiceImpl"/>

Como podemos observar, el servicio ha sido definido como servicio de aplicación y además se le establecen dos parámetros.

  • serviceInterface → la interfaz del servicio
  • serviceImplementation → la implementación de dicha interfaz

La ventaja de esto es que a futuro podemos crear diferentes implementaciones del servicio para crear integraciones con distintas API’s.

A continuación definimos la interfaz del servicio:

public interface ApiService {
    String aiRequest(String prompt);
}

Y, por último, su implementación:

public class OpenAIApiServiceImpl implements ApiService {
    @Override
    public String aiRequest(final String prompt) {
        OpenAiService service = new OpenAiService(AppSettingsState.getInstance().getOpenAIKey());
        CompletionRequest completionRequest = CompletionRequest.builder()
                .prompt(prompt)
                .model("text-davinci-003")
                .echo(false)
                .maxTokens(1500)
                .temperature(0.7)
                .build();

        return service.createCompletion(completionRequest).getChoices().get(0).getText();
    }
}

Para la integración con la API de OpenAI estamos usando la librería de Java que se nos especifica en su documentación.

Dicha librería necesita varios parámetros. El más importante es la API Key que, como hemos dicho, íbamos a guardar en las propiedades del IDE, y que veremos más adelante cómo se crea. De momento en el código podemos observar cómo se invoca a la librería y cómo se extrae la Key de las settings del IDE:

new OpenAiService(AppSettingsState.getInstance().getOpenAIKey());

Posteriormente se establece que el tipo de request va a ser de tipo «Completion», y se le pasan una serie de parámetros:

  • .prompt(prompt) → el prompt junto con el contexto que se le provee a la IA
  • .model(«text-davinci-003») → el modelo de Codex
  • .echo(false) → con esta opción indicamos si queremos que el prompt auto completado por la IA sea mostrado en la respuesta. En nuestro caso no nos interesa.
  • .maxTokens(1500) → La cantidad máxima de tokens de la petición.
  • .temperature(0.7) → la temperatura de la petición. A más temperatura, más creatividad tendrá la IA a la hora de responder.

A su vez, el IntelliJ Platform nos ofrece también unos Light services los cuales no han de ser definidos en el archivo plugin.xml. En cuyo caso se utiliza la anotación @Service para definirlos.

En nuestro caso, dado que el acceso a componentes Swing es algo complicado de manera directa, vamos a crear un servicio en el que «registremos» dichos componentes en el momento de la creación, pudiendo acceder a ellos cuando necesitemos actualizarlos (a la hora de copiar código en el apartado de Contexto o escribir la respuesta de la IA en el apartado de Respuesta).

@Service
public final class ComponentService {

    private Map<String, JComponent> components;

    public void addComponent(final String componentName, final JComponent component) {
        if (components == null) {
            components = new HashMap<>();
        }
        components.put(componentName, component);
    }

    public JComponent getComponentByName(final String componentName) {
        return components.get(componentName);
    }
}

Utilizamos el servicio para añadir componentes a un Map y posteriormente poder recuperarlos por su nombre.

5. Menú de configuración


Otro concepto nuevo añadido es el de Settings. Los settings almacenan los estados que controlan el comportamiento y la apariencia del IDE.

En nuestro caso, necesitamos almacenar la Key para hacer uso de la API de la IA que estamos usando. Para ello necesitamos configurar un primer servicio para controlar la persistencia de nuestra información. En este caso, la key. Para ello, añadimos el siguiente snippet en plugin.xml, dentro del tag extensions:

<applicationService
                serviceImplementation="com.autentia.eris.settings.AppSettingsState"/>

Una vez añadido, procedemos a implementar el servicio:

@State(
        name = "com.autentia.eris.settings.AppSettingsState",
        storages = @Storage("ErisSettingsPlugin.xml")
)
public class AppSettingsState implements PersistentStateComponent<AppSettingsState> {

    public String getOpenAIKey() {
        return openAIKey;
    }

    public void setOpenAIKey(String openAIKey) {
        this.openAIKey = openAIKey;
    }

    private String openAIKey = "";

    public static AppSettingsState getInstance() {
        return ApplicationManager.getApplication().getService(AppSettingsState.class);
    }
    @Override
    public @Nullable AppSettingsState getState() {
        return this;
    }

    @Override
    public void loadState(@NotNull final AppSettingsState state) {
        XmlSerializerUtil.copyBean(state, this);
    }
}

De la implementación del código prestamos atención a lo siguiente:

@State(
        name = "com.autentia.eris.settings.AppSettingsState",
        storages = @Storage("ErisSettingsPlugin.xml")
)

La anotación @State define la localización de la persistencia.

  • name → es el FQN (full qualified name) de la clase.
  • storages → usando @storage definimos el nombre del archivo persistido. En este caso se persiste en el directorio options.

Una vez tenemos el servicio de persistencia, pasamos a crear nuestro menú dentro del menú de Settings del IDE. Para ello añadimos lo siguiente al fichero plugin.xml, dentro del tag extensions:

<applicationConfigurable
                parentId="tools"
                instance="com.autentia.eris.settings.AppSettingsConfigurable"
                id="com.autentia.eris.settings.AppSettingsConfigurable"
                displayName="Eris Settings"/>
  • parentId → establece el padre del menú. En nuestro caso, el menú de configuración estará dentro del grupo Tools.
  • instance → el FQN de la implementación.
  • id → la id de la configuración, estableciendo el mismo que el FQN.
  • displayName → El texto mostrado dentro del apartado Settings.

La implementación del menú es la siguiente:

public class AppSettingsConfigurable implements Configurable {

    private AppSettingsComponent mySettingsComponent;

    @Override
    public @NlsContexts.ConfigurableName String getDisplayName() {
        return "Eris Settings";
    }

    @Override
    public JComponent getPreferredFocusedComponent() {
        return mySettingsComponent.getPreferredFocusedComponent();
    }

    @Override
    public @Nullable JComponent createComponent() {
        mySettingsComponent = new AppSettingsComponent();
        return mySettingsComponent.getPanel();
    }

    @Override
    public boolean isModified() {
        AppSettingsState settings = AppSettingsState.getInstance();
        return !Objects.equals(mySettingsComponent.getApiKey(), settings.getOpenAIKey());
    }

    @Override
    public void apply() {
        AppSettingsState settings = AppSettingsState.getInstance();
        settings.setOpenAIKey(mySettingsComponent.getApiKey());
    }

    @Override
    public void reset() {
        AppSettingsState settings = AppSettingsState.getInstance();
        mySettingsComponent.setApiKey(settings.getOpenAIKey());
    }

    @Override
    public void disposeUIResources() {
        mySettingsComponent = null;
    }
}

 

6. Conclusiones


Ante el nuevo tema de moda que es el uso de la IA en distintos ámbitos de la sociedad, nos asalta la duda de la confiabilidad y la protección de nuestros datos a la hora de su uso. Cuando lo trasladamos al ámbito profesional, y más concretamente al de la programación, las compañías son muy recelosas de usar estas herramientas novedosas. Por lo tanto, soluciones como las que aporta nuestro plugin Eris son un paso para proveer ese grado de confiabilidad, y tener el control en todo momento de lo que sale de nuestros equipos. Además, haciéndolo open source, y explicando su proceso de creación, ayuda a tal fin, apoya el movimiento Open Source, y anima a la contribución para hacer que dicho plugin sea una herramienta más fuerte en el futuro.

DEJA UNA RESPUESTA

Por favor ingrese su comentario!

He leído y acepto la política de privacidad

Por favor ingrese su nombre aquí

Información básica acerca de la protección de datos

  • Responsable:
  • Finalidad:
  • Legitimación:
  • Destinatarios:
  • Derechos:
  • Más información: Puedes ampliar información acerca de la protección de datos en el siguiente enlace:política de privacidad