Skip to content
Merged
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
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
package com.ks.perplexity;

import org.springframework.ai.openai.OpenAiChatModel;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Profile;

@Configuration(proxyBeanMethods = false)
@Profile("perplexity")
public class ApplicationConfiguration {

@Bean
public ChatService chatService(@Autowired OpenAiChatModel openAiChatModel) {
return new ChatService(openAiChatModel);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
package com.ks.perplexity;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
public class ChatApplication {
public static void main(String[] args) {
SpringApplication.run(ChatApplication.class, args);
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
package com.ks.perplexity;

import org.springframework.ai.chat.prompt.Prompt;
import org.springframework.ai.openai.OpenAiChatModel;
import org.springframework.ai.openai.OpenAiChatOptions;

public class ChatService {
private OpenAiChatModel chatModel;

public ChatService(OpenAiChatModel chatModel) {
this.chatModel = chatModel;
}

public String chat(String userInstruction) {
chatModel.call(userInstruction);
return chatModel.call(userInstruction);
}

public String chat(String userInstruction, String model, String temperature) {
OpenAiChatOptions chatOptions = OpenAiChatOptions.builder()
.model(model)
.temperature(Double.parseDouble(temperature))
.build();
Prompt prompt = Prompt.builder()
.content(userInstruction)
.chatOptions(chatOptions)
.build();
return chatModel.call(prompt)
.getResult()
.getOutput()
.getText();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
spring.application.name=spring-ai-perplexity

spring.ai.openai.api-key=pplx-xxxxxxx
spring.ai.openai.base-url=https://api.perplexity.ai
spring.ai.openai.chat.completions-path=/chat/completions

spring.ai.openai.chat.options.model=sonar-pro
spring.ai.openai.chat.options.temperature=0.2
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
@startuml
'https://plantuml.com/class-diagram
set namespaceSeparator none
scale 1
skinparam padding 0
skinparam ranksep 50
hide empty attributes
skinparam Handwritten false
skinparam ClassBorderColor black
skinparam BackgroundColor #F0EDDE
skinparam ClassAttributeFontColor #222222
skinparam ClassFontStyle bold

skinparam class {
ArrowColor #3C88A3
ArrowFontColor #3C88A3
hide empty attributes
skinparam Handwritten false
skinparam ClassBorderColor black
BackgroundColor #FFFFFF
}

interface ChatOptions {
+getTemperature()
+getMaxTokens()
}

class OpenAiChatOptions {
+getTemperature(): double
+getMaxTokens(): int
+getModel(): String
+builder():OpenAiChatOptions.Builder
}

class "OpenAiChatOptions.Builder" as ob {
+temperature(double):OpenAiChatOptions.Builder
+maxTokens(int):OpenAiChatOptions.Builder
+model(String):OpenAiChatOptions.Builder
+build(): OpenAiChatOptions
}


class "OpenAiChatModel" as om {
+call(Prompt prompt):ChatResponse
+call(Prompt prompt, OpenAiChatOptions chatOptions):ChatResponse
+call(String prompt):String
+getDefaultOptions():ChatOptions
}

class "Prompt" as p {
+Prompt(String prompt, ChatOptions chatOptions)
+builder(): Prompt.Builder
}

class "ChatResponse" as cr {
+getResult(): Generation
}

ChatOptions <|.up. OpenAiChatOptions:implements

ob ..> OpenAiChatOptions : builds

om .down.> p : uses
om -up-> cr : call(Prompt):ChatResponse
om -right-> OpenAiChatOptions : uses


@enduml
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
package com.ks.perplexity;

import static org.assertj.core.api.Assertions.assertThat;

import org.junit.jupiter.api.Test;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.ai.chat.prompt.ChatOptions;
import org.springframework.ai.openai.OpenAiChatModel;
import org.springframework.ai.openai.OpenAiChatOptions;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.ActiveProfiles;

@SpringBootTest
@ActiveProfiles("perplexity")
public class PerplexityOpenAiLiveTest {

private final Logger logger = LoggerFactory.getLogger(PerplexityOpenAiLiveTest.class);

@Autowired
private ChatService chatservice;

@Autowired
private OpenAiChatModel openAiChatModel;

@Test
void whenPerplexityChatClientAutoConfigured_thenExecute() {
String userPrompt = """
Context:
1. The book "The Stand" is a fantasy novel by Stephen King.
2. "Sense and Sensibility" is a romance novel by Jane Austen,
published by Penguin Books.
3. "The ABC Murders" is a mystery novel by Agatha Christie,
published by HarperCollins.
Question:
Name a book written by Agatha Christie,
based strictly on the context above.
""";
String response = chatservice.chat(userPrompt);
logger.info("Response from Perplexity: {}", response);
assertThat(response).isNotNull()
.isNotEmpty()
.containsIgnoringCase("The ABC Murders")
.doesNotContainIgnoringCase("The Stand", "Sense and Sensibility");
}

@Test
void whenPerplexityChatClientAutoConfigured_thenExamineClientProperties() {
ChatOptions chatOptions = openAiChatModel.getDefaultOptions();

assertThat(chatOptions).isInstanceOf(OpenAiChatOptions.class);

assertThat(chatOptions.getModel()).isEqualTo("sonar-pro");

assertThat(chatOptions.getTemperature()).isEqualTo(0.2);
}

@Test
void whenPerplexityChatClientCustomized_thenExecute() {
String userPrompt = """
Who won the latest FIFA World Cup? Just the name of the country without any citations, please.
""";
String response = chatservice.chat(userPrompt, "sonar", "0.1");
logger.info("Response from Perplexity: {}", response);
assertThat(response).isNotNull()
.isNotEmpty()
.containsIgnoringCase("Argentina");
}
}