Skip to content

Commit

Permalink
feat: add support for custom title
Browse files Browse the repository at this point in the history
work on #9
  • Loading branch information
bsorrentino committed Jul 17, 2024
1 parent 0e9bc76 commit 48ec649
Show file tree
Hide file tree
Showing 7 changed files with 112 additions and 2,717 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -20,14 +20,20 @@

import java.io.IOException;
import java.io.PrintWriter;
import java.net.URI;
import java.util.HashMap;
import java.util.Map;
import java.util.Objects;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.TimeUnit;


/**
* LangGraphStreamingServer is an interface that represents a server that supports streaming
* of LangGraph.
* Implementations of this interface can be used to create a web server
* that exposes an API for interacting with compiled language graphs.
*
*/
public interface LangGraphStreamingServer {

Logger log = LoggerFactory.getLogger(LangGraphStreamingServer.class);
Expand All @@ -41,12 +47,17 @@ static Builder builder() {
class Builder {
private int port = 8080;
private Map<String,ArgumentMetadata> inputArgs = new HashMap<>();
private String title = null;

public Builder port(int port) {
this.port = port;
return this;
}

public Builder title(String title) {
this.title = title;
return this;
}
public Builder addInputStringArg(String name, boolean required) {
inputArgs.put(name, new ArgumentMetadata("string", required) );
return this;
Expand Down Expand Up @@ -78,7 +89,9 @@ public <State extends AgentState> LangGraphStreamingServer build(CompiledGraph<S
// context.setContextPath("/");
// Add the streaming servlet
context.addServlet(new ServletHolder(new GraphExecutionServlet<State>(compiledGraph)), "/stream");
context.addServlet(new ServletHolder(new GraphInitServlet<State>(compiledGraph, inputArgs)), "/init");

InitData initData = new InitData( title, inputArgs );
context.addServlet(new ServletHolder(new GraphInitServlet<State>(compiledGraph, initData)), "/init");

Handler.Sequence handlerList = new Handler.Sequence(resourceHandler, context);

Expand Down Expand Up @@ -158,29 +171,37 @@ protected void doPost(HttpServletRequest request, HttpServletResponse response)
}

record ArgumentMetadata (
String type,
boolean required
) {}
String type,
boolean required ) {}

record InitData(
String title,
Map<String, ArgumentMetadata> args ) {}

/**
* return the graph representation in mermaid format
*/
class GraphInitServlet<State extends AgentState> extends HttpServlet {

final CompiledGraph<State> compiledGraph;
final Map<String, ArgumentMetadata> inputArgs;
final ObjectMapper objectMapper = new ObjectMapper();
final InitData initData;

record Result (
String graph,
String title,
Map<String, ArgumentMetadata> args
) {}
) {

public Result(GraphRepresentation graph, InitData initData ) {
this( graph.getContent(), initData.title(), initData.args() ); // graph.getContent();
}
}

public GraphInitServlet(CompiledGraph<State> compiledGraph, Map<String, ArgumentMetadata> inputArgs) {
public GraphInitServlet(CompiledGraph<State> compiledGraph, InitData initData) {
Objects.requireNonNull(compiledGraph, "compiledGraph cannot be null");
this.compiledGraph = compiledGraph;
this.inputArgs = inputArgs;
this.initData = initData;
}

@Override
Expand All @@ -190,7 +211,7 @@ protected void doGet(HttpServletRequest request, HttpServletResponse response) t

GraphRepresentation graph = compiledGraph.getGraph(GraphRepresentation.Type.MERMAID);

final Result result = new Result(graph.getContent(), inputArgs);
final Result result = new Result(graph, initData);
String resultJson = objectMapper.writeValueAsString(result);
// Start asynchronous processing
final PrintWriter writer = response.getWriter();
Expand Down
4 changes: 3 additions & 1 deletion jetty/src/main/js/deploy.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,5 +3,7 @@ import { $ } from "bun";

$.nothrow();
await $`bun build.ts`
await $`rm ../webapp/*`
await $`bun twgen.ts --no-watch`
await $`bun build.ts`
await $`rm ../resources/webapp/*`
await $`cp dist/* ../resources/webapp`
6 changes: 3 additions & 3 deletions jetty/src/main/js/index.html
Original file line number Diff line number Diff line change
@@ -1,18 +1,18 @@
<!DOCTYPE html>
<html lang="en" data-theme="cupcake">
<html lang="en" data-theme="dark">
<head>
<meta charset="UTF-8" />
<link rel="icon" type="image/svg+xml" href="favicon.svg" />
<link href="./app.css" type="text/css" rel="stylesheet">
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Vite + Lit App</title>
<title>LangGraph4j App</title>
<script type="module" src="/src/lg4j-workbench.js"></script>
<script type="module" src="/src/lg4j-executor.js"></script>
<script type="module" src="/src/lg4j-graph.js"></script>
<script type="module" src="/src/lg4j-result.js"></script>
</head>
<body>
<lg4j-workbench>
<lg4j-workbench title="LangGraph4j">
<lg4j-graph slot="graph">
graph TD;
A-->B;
Expand Down
35 changes: 20 additions & 15 deletions jetty/src/main/js/src/lg4j-executor.js
Original file line number Diff line number Diff line change
Expand Up @@ -98,10 +98,10 @@ export class LG4JExecutorElement extends LitElement {

const initData = await initResponse.json()

console.debug( initData );
console.debug( 'initData', initData );

this.dispatchEvent( new CustomEvent( 'graph', {
detail: initData.graph,
this.dispatchEvent( new CustomEvent( 'init', {
detail: initData,
bubbles: true,
composed: true,
cancelable: true
Expand All @@ -114,18 +114,21 @@ export class LG4JExecutorElement extends LitElement {
async #init_test() {

await delay( 1000 );
this.dispatchEvent( new CustomEvent( 'graph', {
detail: `
flowchart TD
start((start))
stop((stop))
node1("node1")
node2("node2")
start:::start --> node1:::node1
node1:::node1 --> node2:::node2
node2:::node2 --> stop:::stop
`,
this.dispatchEvent( new CustomEvent( 'init', {
detail: {
title: 'LangGraph4j : TEST',
graph:`
flowchart TD
start((start))
stop((stop))
node1("node1")
node2("node2")
start:::start --> node1:::node1
node1:::node1 --> node2:::node2
node2:::node2 --> stop:::stop
`
},
bubbles: true,
composed: true,
cancelable: true
Expand Down Expand Up @@ -202,8 +205,10 @@ export class LG4JExecutorElement extends LitElement {
}));
}

await send( 'start' );
await send( 'node1' );
await send( 'node2');
await send( 'stop' );

}

Expand Down
79 changes: 41 additions & 38 deletions jetty/src/main/js/src/lg4j-workbench.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,65 +4,63 @@ import { html, css, LitElement } from 'lit';

export class LG4JWorkbenchElement extends LitElement {

static styles = [css`
.item-graph {
grid-area: left;
// background-color: red;
}
.item-result {
grid-area: right;
// background-color: blue;
}
.item-executor {
grid-area: bottom;
//background-color: yellow;
}
.item-container {
height: 100vh;
display: grid;
grid-template-columns: 1fr 1fr 1fr 1fr;
grid-template-rows: 25% 25% 25% 25% ;
row-gap: 15px;
grid-template-areas:
"left left right right"
"left left right right"
"left left right right"
"bottom bottom right right";
}
`, TWStyles];
;
static styles = [css``, TWStyles];

static properties = {
title: {},
}

constructor() {
super();

#routeEvent( e ) {

}

#routeEvent( e, slot ) {

const { type, detail } = e

const slot = type.split('-')[0]
if( !slot ) {
slot = type.split('-')[0]
}

console.debug( 'routeEvent', type, slot )

const event = new CustomEvent( type, { detail } );

const elem = this.querySelector(`[slot="${slot}"]`)
if( !elem ) {
console.error( `slot "${slot}" not found!` )
return
}
elem.dispatchEvent( new CustomEvent( type, { detail } ) )
elem.dispatchEvent( event )

}

#routeInitEvent( e ) {
const { graph, title } = e.detail

this.#routeEvent( new CustomEvent( "graph", { detail: graph }));

if( title ) {
this.title = title
this.requestUpdate()
}
}

connectedCallback() {
super.connectedCallback()

this.addEventListener( "init", this.#routeInitEvent );
this.addEventListener( "result", this.#routeEvent );
this.addEventListener( "graph", this.#routeEvent );
this.addEventListener( "graph-active", this.#routeEvent );
}

disconnectedCallback() {
super.disconnectedCallback()

this.removeEventListener( "init", this.#routeInitEvent );
this.removeEventListener( "result", this.#routeEvent );
this.removeEventListener( "graph", this.#routeEvent );
this.removeEventListener( "graph-active", this.#routeEvent );
}

Expand All @@ -71,10 +69,15 @@ export class LG4JWorkbenchElement extends LitElement {

render() {
return html`
<div class="item-container">
<div class="item-graph border border-gray-300 p-5 flex items-center justify-center" id="panel1"><slot name="graph">LEFT</slot></div>
<div class="item-result" id="panel3"><slot name="result">RIGHT</slot></div>
<div class="item-executor" id="panel2"><slot name="executor">BOTTOM</slot></div>
<div class="grid grid-cols-2 gap-y-2 grid-rows-[60px_auto_auto_auto_auto_auto] h-screen">
<div class="col-span-2">
<div class="navbar bg-base-100">
<a class="btn btn-ghost text-xl">${this.title}</a>
</div>
</div>
<div class="row-span-4 border border-gray-300 p-5 flex items-center justify-center"><slot name="graph">LEFT</slot></div>
<div class="row-span-5"><slot name="result">RIGHT</slot></div>
<div><slot name="executor">BOTTOM</slot></div>
</div>
`;
}
Expand Down
Loading

0 comments on commit 48ec649

Please sign in to comment.