Skip to content

Commit

Permalink
S04E01
Browse files Browse the repository at this point in the history
  • Loading branch information
iceener committed Nov 25, 2024
1 parent 1c9c7f2 commit 1786cfe
Show file tree
Hide file tree
Showing 11 changed files with 2,595 additions and 0 deletions.
96 changes: 96 additions & 0 deletions todo/OpenAIService.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
import OpenAI, { toFile } from "openai";
import type { ChatCompletionMessageParam } from "openai/resources/chat/completions";
import fs from 'fs/promises';
import type { CreateEmbeddingResponse } from 'openai/resources/embeddings';

export interface ImageProcessingResult {
description: string;
source: string;
}

export class OpenAIService {
private openai: OpenAI;

constructor() {
this.openai = new OpenAI();
}

async completion(config: {
messages: ChatCompletionMessageParam[],
model?: string,
stream?: boolean,
jsonMode?: boolean,
maxTokens?: number
}): Promise<OpenAI.Chat.Completions.ChatCompletion | AsyncIterable<OpenAI.Chat.Completions.ChatCompletionChunk>> {
const { messages, model = "gpt-4o", stream = false, jsonMode = false, maxTokens = 4096 } = config;
try {
const chatCompletion = await this.openai.chat.completions.create({
messages,
model,
...(model !== 'o1-mini' && model !== 'o1-preview' && {
stream,
max_tokens: maxTokens,
response_format: jsonMode ? { type: "json_object" } : { type: "text" }
})
});

return stream
? chatCompletion as AsyncIterable<OpenAI.Chat.Completions.ChatCompletionChunk>
: chatCompletion as OpenAI.Chat.Completions.ChatCompletion;
} catch (error) {
console.error("Error in OpenAI completion:", error);
throw error;
}
}

async createEmbedding(text: string): Promise<number[]> {
try {
const response: CreateEmbeddingResponse = await this.openai.embeddings.create({
model: "text-embedding-3-large",
input: text,
});
return response.data[0].embedding;
} catch (error) {
console.error("Error creating embedding:", error);
throw error;
}
}

async processImage(imagePath: string): Promise<ImageProcessingResult> {
try {
const image = await fs.readFile(imagePath);
const base64Image = image.toString('base64');

const response = await this.openai.chat.completions.create({
model: "gpt-4-vision-preview",
messages: [
{
role: "user",
content: [
{ type: "text", text: "Describe this image in detail." },
{ type: "image_url", image_url: { url: `data:image/jpeg;base64,${base64Image}` } },
],
},
],
});

return {
description: response.choices[0].message.content || "No description available.",
source: imagePath,
};
} catch (error) {
console.error(`Error processing image ${imagePath}:`, error);
throw error;
}
}

async processImages(imagePaths: string[]): Promise<ImageProcessingResult[]> {
try {
const results = await Promise.all(imagePaths.map(path => this.processImage(path)));
return results;
} catch (error) {
console.error("Error processing multiple images:", error);
throw error;
}
}
}
122 changes: 122 additions & 0 deletions todo/TasksService.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,122 @@
import { v9 as Todoist } from 'todoist';

export class TasksService {
private todoist: any;

constructor(token: string) {
this.todoist = Todoist(token);
}

async sync() {
await this.todoist.sync();
}

async addTasks(tasks: { name: string, project_id?: string, due?: string, description?: string }[]) {

const promises = tasks.map(async task => {
const newItem = await this.todoist.items.add({
content: task.name,
project_id: task.project_id,
due: { date: task.due },
description: task.description
});

return newItem;
});
return Promise.all(promises);
}

async updateTasks(tasks: { task_id: string, project_id?: string, content?: string, due?: string, status?: string, description?: string }[]) {
const promises = tasks.map(async task => {
let previousProjectId: string | undefined;

if (task.project_id) {
previousProjectId = task.project_id;
await this.todoist.items.move({ id: task.task_id, project_id: task.project_id });
}

const updateData: any = {
id: task.task_id,
content: task.content || undefined,
due: task.due ? { date: task.due } : undefined,
project_id: task.project_id || undefined,
checked: task.status === 'DONE',
description: task.description || undefined
};

if (task.status === 'DONE') {
await this.todoist.items.close({ id: task.task_id });
}

await this.todoist.items.update(updateData);

const updatedTask = this.todoist.items.get().find(item => item.id === task.task_id);

if (previousProjectId) {
updatedTask.previous_project = previousProjectId;
}

return updatedTask;
});
return Promise.all(promises);
}

async deleteTasks(taskIds: string[]) {

const promises = taskIds.map(async id => {
await this.todoist.items.delete({ id });

return id; // Return the ID of the deleted task
});
return Promise.all(promises);
}

async listProjects() {
return this.todoist.projects.get();
}

async getProjectData(projectId: string) {
await this.todoist.sync();
return this.todoist.projects.get();
}

async syncData() {
await this.todoist.sync();
return {
projects: this.todoist.projects.get(),
tasks: this.todoist.items.get()
};
}

async listTasksFromProjects(projectIds: string[], statuses?: string[], startDate?: string, endDate?: string) {
const { projects, tasks } = await this.syncData();

const relevantProjects = projects.filter(project => projectIds.includes(project.id));
const relevantTasks = tasks.filter(task => projectIds.includes(task.project_id));

// Apply filters
return relevantTasks.filter((task: any) => {
// Status check
if (statuses && statuses.length > 0) {
const taskStatus = task.checked ? 'DONE' : 'ACTIVE';
if (!statuses.includes(taskStatus)) return false;
}

// Date filtering
if (startDate || endDate) {
const taskDate = task.due?.date ? new Date(task.due.date + 'T00:00:00Z') : null;
if (taskDate) {
if (startDate && taskDate < new Date(startDate)) return false;
if (endDate && taskDate > new Date(endDate)) return false;
}
}

return true;
});
}

async getTaskDetails(taskId: string) {

return this.todoist.items.get().find(task => task.id === taskId);
}
}
Loading

0 comments on commit 1786cfe

Please sign in to comment.