-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
refactor(dictionary): extract dictionary trait
#2 : placedholder
- Loading branch information
1 parent
9702567
commit 5099d89
Showing
5 changed files
with
102 additions
and
64 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,22 +1,23 @@ | ||
[package] | ||
name = "logseq-plugin-vocabulary-card" | ||
version = "0.1.0" | ||
version = "0.1.1" | ||
authors = ["Richard Hao <[email protected]>"] | ||
edition = "2021" | ||
|
||
[lib] | ||
crate-type = ["cdylib", "rlib"] | ||
|
||
[dependencies] | ||
wasm-bindgen = "0.2.89" | ||
wasm-bindgen-futures = "0.4.39" | ||
serde-wasm-bindgen = "0.6.3" | ||
serde = { version = "1.0.194", features = ["derive"] } | ||
serde_json = "1.0.110" | ||
reqwest = { version = "0.11.23", features = ["blocking", "json"] } | ||
wasm-bindgen = "0.2.92" | ||
wasm-bindgen-futures = "0.4.42" | ||
serde-wasm-bindgen = "0.6.5" | ||
serde = { version = "1.0.204", features = ["derive"] } | ||
serde_json = "1.0.122" | ||
reqwest = { version = "0.12.5", features = ["blocking", "json"] } | ||
async-trait = "0.1.81" | ||
|
||
[dev-dependencies] | ||
wasm-bindgen-test = "0.3.39" | ||
wasm-bindgen-test = "0.3.42" | ||
|
||
[profile.release] | ||
# Tell `rustc` to optimize for small code size. | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,51 @@ | ||
use async_trait::async_trait; | ||
|
||
use crate::{http_request, Dictionary, WordDefinition}; | ||
|
||
pub struct GeminiDictionary { | ||
api_key: String, | ||
} | ||
|
||
impl GeminiDictionary { | ||
pub fn new(api_key: &str) -> Self { | ||
Self { | ||
api_key: api_key.trim().to_string(), | ||
} | ||
} | ||
} | ||
|
||
#[async_trait(?Send)] | ||
impl Dictionary for GeminiDictionary { | ||
async fn define(&self, word: &str) -> Result<WordDefinition, String> { | ||
let url = format!( | ||
"https://generativelanguage.googleapis.com/v1beta/models/gemini-pro:generateContent?key={}", | ||
self.api_key | ||
); | ||
let req_body = serde_json::json!({ | ||
"contents": [{ | ||
"parts": [{ | ||
"text": self.default_prompt(word) | ||
}] | ||
}] | ||
}); | ||
|
||
let res_body = http_request::make_request(&url, req_body).await?; | ||
let dictionary = extract_dictionary(res_body)?; | ||
Ok(dictionary) | ||
} | ||
} | ||
|
||
fn extract_dictionary(res_body: serde_json::Value) -> Result<WordDefinition, String> { | ||
let Some(dictionary_str) = res_body["candidates"][0]["content"]["parts"][0]["text"].as_str() | ||
else { | ||
return Err("json error: dictionary not found".to_string()); | ||
}; | ||
let dictionary_str = dictionary_str | ||
.trim_start_matches("```json") | ||
.trim_start_matches("```") | ||
.trim_end_matches("```") | ||
.trim(); | ||
let definition = serde_json::from_str::<WordDefinition>(dictionary_str) | ||
.map_err(|e| format!("serde error: {}", e))?; | ||
Ok(definition) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,19 @@ | ||
use reqwest::StatusCode; | ||
|
||
pub async fn make_request(url: &str, body: serde_json::Value) -> Result<serde_json::Value, String> { | ||
let req_client = reqwest::Client::new(); | ||
let res = req_client | ||
.post(url) | ||
.json(&body) | ||
.send() | ||
.await | ||
.map_err(|e| format!("request error: {}", e))?; | ||
if res.status() != StatusCode::OK { | ||
return Err(format!("request error: {}", res.status())); | ||
} | ||
let json = res | ||
.json::<serde_json::Value>() | ||
.await | ||
.map_err(|e| format!("json error: {}", e))?; | ||
Ok(json) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,73 +1,40 @@ | ||
use reqwest::StatusCode; | ||
mod gemini_dictionary; | ||
mod http_request; | ||
|
||
use async_trait::async_trait; | ||
use gemini_dictionary::GeminiDictionary; | ||
use serde::{Deserialize, Serialize}; | ||
use wasm_bindgen::prelude::*; | ||
|
||
#[wasm_bindgen] | ||
#[derive(Serialize, Deserialize)] | ||
pub struct Word { | ||
pub struct WordDefinition { | ||
word: String, | ||
pronunciation: String, | ||
definition: String, | ||
examples: Vec<String>, | ||
image: String, | ||
} | ||
|
||
#[wasm_bindgen] | ||
pub async fn define_word(word: &str, api_key: &str) -> Result<JsValue, JsValue> { | ||
let url = format!( | ||
"https://generativelanguage.googleapis.com/v1beta/models/gemini-pro:generateContent?key={}", | ||
api_key.trim() | ||
); | ||
let prompt = format!( | ||
"please act as a dictionary, including the pronunciation, explanation, two examples of sentences, and one image. And the word is {}. please output the result in json format, the json keys are `word`, `pronunciation`, `definition`, `examples`, and `image`.", | ||
word.trim() | ||
); | ||
let req_body = serde_json::json!({ | ||
"contents": [{ | ||
"parts": [{ | ||
"text": prompt | ||
}] | ||
}] | ||
|
||
}); | ||
|
||
let res_body = make_request(&url, req_body) | ||
.await | ||
.map_err(|e| JsValue::from_str(&e))?; | ||
let dictionary = extract_dictionary(res_body).map_err(|e| JsValue::from_str(&e))?; | ||
const PROMPT_TEMPLATE: &str = "please act as a dictionary, including the pronunciation, explanation, two examples of sentences, and one image. And the word is `{:word}`. please output the result in json format, the json keys are `word`, `pronunciation`, `definition`, `examples`, and `image`."; | ||
|
||
Ok(serde_wasm_bindgen::to_value(&dictionary)?) | ||
#[async_trait(?Send)] | ||
pub trait Dictionary { | ||
// fn set_llm_model(&self, model: &str) -> Result<&Self, String>; | ||
async fn define(&self, word: &str) -> Result<WordDefinition, String>; | ||
// fn get_llm_models(&self) -> Result<Vec<String>, String>; | ||
fn default_prompt(&self, word: &str) -> String { | ||
PROMPT_TEMPLATE.replace("{:word}", word.trim()) | ||
} | ||
} | ||
|
||
async fn make_request(url: &str, body: serde_json::Value) -> Result<serde_json::Value, String> { | ||
let req_client = reqwest::Client::new(); | ||
let res = req_client | ||
.post(url) | ||
.json(&body) | ||
.send() | ||
.await | ||
.map_err(|e| format!("request error: {}", e))?; | ||
if res.status() != StatusCode::OK { | ||
return Err(format!("request error: {}", res.status())); | ||
} | ||
let json = res | ||
.json::<serde_json::Value>() | ||
#[wasm_bindgen] | ||
pub async fn define_word(word: &str, api_key: &str) -> Result<JsValue, JsValue> { | ||
let dictionary = GeminiDictionary::new(api_key); | ||
let word_defination = dictionary | ||
.define(word) | ||
.await | ||
.map_err(|e| format!("json error: {}", e))?; | ||
Ok(json) | ||
} | ||
.map_err(|e| JsValue::from_str(&e))?; | ||
|
||
fn extract_dictionary(res_body: serde_json::Value) -> Result<Word, String> { | ||
let Some(dictionary_str) = res_body["candidates"][0]["content"]["parts"][0]["text"].as_str() | ||
else { | ||
return Err("json error: dictionary not found".to_string()); | ||
}; | ||
let dictionary_str = dictionary_str | ||
.trim_start_matches("```json") | ||
.trim_start_matches("```") | ||
.trim_end_matches("```") | ||
.trim(); | ||
let dictionary = | ||
serde_json::from_str::<Word>(dictionary_str).map_err(|e| format!("serde error: {}", e))?; | ||
Ok(dictionary) | ||
Ok(serde_wasm_bindgen::to_value(&word_defination)?) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters