Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions runtime/bundles/org.eclipse.core.llm/.classpath
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<classpath>
<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-21"/>
<classpathentry kind="con" path="org.eclipse.pde.core.requiredPlugins"/>
<classpathentry kind="src" path="src"/>
<classpathentry kind="output" path="bin"/>
</classpath>
28 changes: 28 additions & 0 deletions runtime/bundles/org.eclipse.core.llm/.project
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
<?xml version="1.0" encoding="UTF-8"?>
<projectDescription>
<name>org.eclipse.core.llm</name>
<comment></comment>
<projects>
</projects>
<buildSpec>
<buildCommand>
<name>org.eclipse.jdt.core.javabuilder</name>
<arguments>
</arguments>
</buildCommand>
<buildCommand>
<name>org.eclipse.pde.ManifestBuilder</name>
<arguments>
</arguments>
</buildCommand>
<buildCommand>
<name>org.eclipse.pde.SchemaBuilder</name>
<arguments>
</arguments>
</buildCommand>
</buildSpec>
<natures>
<nature>org.eclipse.pde.PluginNature</nature>
<nature>org.eclipse.jdt.core.javanature</nature>
</natures>
</projectDescription>
11 changes: 11 additions & 0 deletions runtime/bundles/org.eclipse.core.llm/META-INF/MANIFEST.MF
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
Manifest-Version: 1.0
Bundle-ManifestVersion: 2
Bundle-Name: Core LLM
Bundle-SymbolicName: org.eclipse.core.llm;singleton:=true
Bundle-Version: 1.0.0.qualifier
Bundle-Vendor: Eclipse.org
Export-Package: org.eclipse.core.llm
Require-Bundle: org.eclipse.swt,
org.eclipse.jface
Bundle-RequiredExecutionEnvironment: JavaSE-21
Automatic-Module-Name: org.eclipse.core.llm
15 changes: 15 additions & 0 deletions runtime/bundles/org.eclipse.core.llm/about.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1">
<title>About</title>
</head>
<body lang="EN-US">
<h2>About This Content</h2>
<p>The Eclipse Foundation makes available all content in this plug-in
("Content"). Unless otherwise indicated below, the Content is provided
to you under the terms and conditions of the Eclipse Public License
Version 2.0 ("EPL"). A copy of the EPL is available at
<a href="https://www.eclipse.org/legal/epl-2.0/">https://www.eclipse.org/legal/epl-2.0/</a>.</p>
</body>
</html>
6 changes: 6 additions & 0 deletions runtime/bundles/org.eclipse.core.llm/build.properties
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
source.. = src/
output.. = bin/
bin.includes = META-INF/,\
.,\
about.html
src.includes = about.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
/*******************************************************************************
* Copyright (c) 2026 Ericsson
*
* This program and the accompanying materials
* are made available under the terms of the Eclipse Public License 2.0
* which accompanies this distribution, and is available at
* https://www.eclipse.org/legal/epl-2.0/
*
* SPDX-License-Identifier: EPL-2.0
*******************************************************************************/
package org.eclipse.core.llm;

import org.eclipse.jface.dialogs.Dialog;
import org.eclipse.jface.dialogs.IDialogConstants;
import org.eclipse.swt.SWT;
import org.eclipse.swt.layout.GridData;
import org.eclipse.swt.layout.GridLayout;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Control;
import org.eclipse.swt.widgets.Label;
import org.eclipse.swt.widgets.Shell;
import org.eclipse.swt.widgets.Text;

/**
* Simple JFace dialog to configure the URL and model name of a {@link LlmModel}.
* Retrieve the result with {@link #getModel()} after {@link #open()} returns
* {@link org.eclipse.jface.window.Window#OK}.
*/
public class LlmConfigurationDialog extends Dialog {

private String url;
private String model;
private Text urlText;
private Text modelText;
private LlmModel result;

public LlmConfigurationDialog(Shell parent, LlmModel initial) {
super(parent);
this.url = initial != null ? initial.url() : ""; //$NON-NLS-1$
this.model = initial != null ? initial.model() : ""; //$NON-NLS-1$
}

public LlmModel getModel() {
return result;
}

@Override
protected void configureShell(Shell shell) {
super.configureShell(shell);
shell.setText("LLM Configuration"); //$NON-NLS-1$
}

@Override
protected Control createDialogArea(Composite parent) {
Composite area = (Composite) super.createDialogArea(parent);
Composite c = new Composite(area, SWT.NONE);
c.setLayout(new GridLayout(2, false));
c.setLayoutData(new GridData(GridData.FILL_BOTH));

new Label(c, SWT.NONE).setText("URL:"); //$NON-NLS-1$
urlText = new Text(c, SWT.BORDER);
urlText.setText(url);
GridData gd = new GridData(GridData.FILL_HORIZONTAL);
gd.widthHint = 320;
urlText.setLayoutData(gd);

new Label(c, SWT.NONE).setText("Model:"); //$NON-NLS-1$
modelText = new Text(c, SWT.BORDER);
modelText.setText(model);
modelText.setLayoutData(new GridData(GridData.FILL_HORIZONTAL));

return area;
}

@Override
protected void buttonPressed(int buttonId) {
if (buttonId == IDialogConstants.OK_ID) {
result = new LlmModel(urlText.getText().trim(), modelText.getText().trim());
}
super.buttonPressed(buttonId);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
/*******************************************************************************
* Copyright (c) 2026 Ericsson
*
* This program and the accompanying materials
* are made available under the terms of the Eclipse Public License 2.0
* which accompanies this distribution, and is available at
* https://www.eclipse.org/legal/epl-2.0/
*
* SPDX-License-Identifier: EPL-2.0
*******************************************************************************/
package org.eclipse.core.llm;

import java.io.IOException;
import java.net.URI;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
import java.time.Duration;

import org.eclipse.swt.widgets.Display;

/**
* Minimal LLM client. An instance is configured with the endpoint URL and the
* model name and exposes {@link #infer(String)} to query the model.
* <p>
* {@link #infer(String)} performs a blocking HTTP request and must never be
* invoked from the SWT UI thread; doing so throws {@link IllegalStateException}.
* </p>
*/
public final class LlmModel {

private final String url;
private final String model;

public LlmModel(String url, String model) {
this.url = url;
this.model = model;
}

public String url() {
return url;
}

public String model() {
return model;
}

/**
* Send {@code prompt} to the configured LLM and return the raw response body.
*
* @throws IllegalStateException if invoked from the SWT UI thread
* @throws IOException if the HTTP call fails
*/
public String infer(String prompt) throws IOException, InterruptedException {
if (Display.getCurrent() != null) {
throw new IllegalStateException("LlmModel.infer must not be called from the UI thread"); //$NON-NLS-1$
}
String body = "{\"model\":\"" + escape(model) + "\",\"prompt\":\"" + escape(prompt) + "\",\"stream\":false}"; //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
HttpRequest request = HttpRequest.newBuilder(URI.create(url))
.timeout(Duration.ofMinutes(2))
.header("Content-Type", "application/json") //$NON-NLS-1$ //$NON-NLS-2$
.POST(HttpRequest.BodyPublishers.ofString(body))
.build();
HttpResponse<String> response = HttpClient.newHttpClient()
.send(request, HttpResponse.BodyHandlers.ofString());
if (response.statusCode() / 100 != 2) {
throw new IOException("LLM request failed: HTTP " + response.statusCode() + " - " + response.body()); //$NON-NLS-1$ //$NON-NLS-2$
}
return response.body();
}

private static String escape(String s) {
StringBuilder sb = new StringBuilder(s.length() + 8);
for (int i = 0; i < s.length(); i++) {
char c = s.charAt(i);
switch (c) {
case '"' -> sb.append("\\\""); //$NON-NLS-1$
case '\\' -> sb.append("\\\\"); //$NON-NLS-1$
case '\n' -> sb.append("\\n"); //$NON-NLS-1$
case '\r' -> sb.append("\\r"); //$NON-NLS-1$
case '\t' -> sb.append("\\t"); //$NON-NLS-1$
default -> {
if (c < 0x20) {
sb.append(String.format("\\u%04x", (int) c)); //$NON-NLS-1$
} else {
sb.append(c);
}
}
}
}
return sb.toString();
}
}
7 changes: 7 additions & 0 deletions runtime/tests/org.eclipse.core.llm.tests/.classpath
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<classpath>
<classpathentry kind="src" path="src"/>
<classpathentry kind="con" path="org.eclipse.pde.core.requiredPlugins"/>
<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-21"/>
<classpathentry kind="output" path="bin"/>
</classpath>
28 changes: 28 additions & 0 deletions runtime/tests/org.eclipse.core.llm.tests/.project
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
<?xml version="1.0" encoding="UTF-8"?>
<projectDescription>
<name>org.eclipse.core.llm.tests</name>
<comment></comment>
<projects>
</projects>
<buildSpec>
<buildCommand>
<name>org.eclipse.jdt.core.javabuilder</name>
<arguments>
</arguments>
</buildCommand>
<buildCommand>
<name>org.eclipse.pde.ManifestBuilder</name>
<arguments>
</arguments>
</buildCommand>
<buildCommand>
<name>org.eclipse.pde.SchemaBuilder</name>
<arguments>
</arguments>
</buildCommand>
</buildSpec>
<natures>
<nature>org.eclipse.jdt.core.javanature</nature>
<nature>org.eclipse.pde.PluginNature</nature>
</natures>
</projectDescription>
14 changes: 14 additions & 0 deletions runtime/tests/org.eclipse.core.llm.tests/META-INF/MANIFEST.MF
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
Manifest-Version: 1.0
Bundle-ManifestVersion: 2
Bundle-Name: Core LLM Tests
Bundle-SymbolicName: org.eclipse.core.llm.tests;singleton:=true
Bundle-Version: 1.0.0.qualifier
Bundle-Vendor: Eclipse.org
Require-Bundle: org.eclipse.core.llm,
org.eclipse.swt,
org.eclipse.jface
Import-Package: com.sun.net.httpserver,
org.junit.jupiter.api;version="[5.14.0,6.0.0)"
Bundle-RequiredExecutionEnvironment: JavaSE-21
Bundle-ActivationPolicy: lazy
Automatic-Module-Name: org.eclipse.core.llm.tests
8 changes: 8 additions & 0 deletions runtime/tests/org.eclipse.core.llm.tests/about.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd">
<html>
<head><meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1"><title>About</title></head>
<body lang="EN-US">
<h2>About This Content</h2>
<p>The Eclipse Foundation makes available all content in this plug-in ("Content") under the Eclipse Public License Version 2.0
(<a href="https://www.eclipse.org/legal/epl-2.0/">https://www.eclipse.org/legal/epl-2.0/</a>).</p>
</body></html>
6 changes: 6 additions & 0 deletions runtime/tests/org.eclipse.core.llm.tests/build.properties
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
source.. = src/
output.. = bin/
bin.includes = .,\
META-INF/,\
about.html
src.includes = about.html
Loading
Loading